我们的目标是渐变!渐变!
首先了解,渐变分为线性渐变和径向渐变,线性渐变可以是从左上角到右下角的渐变,径向渐变是从中心向外的渐变。
JavaScript中实现渐变可以使用addColorStop的方法,例如创建一个线性渐变的代码为:
// 创建线性渐变
var linearGradient = ctx.createLinearGradient(0, 0, 200, 0);
linearGradient.addColorStop("0", "green");
linearGradient.addColorStop("1", "white");
// 应用渐变到矩形
ctx.fillStyle = linearGradient;
ctx.fillRect(10, 10, 180, 80);
我们使用createLinearGradient(x1, y1, x2, y2)
创建一个线性渐变。参数定义了渐变的开始和结束位置。
径向渐变也是类似的:
// 创建径向渐变
var radialGradient = ctx.createRadialGradient(100, 50, 10, 100, 50, 50);
radialGradient.addColorStop("0", "red");
radialGradient.addColorStop("0.5", "yellow");
radialGradient.addColorStop("1", "green");
// 应用渐变到矩形
ctx.fillStyle = radialGradient;
ctx.fillRect(10, 10, 180, 80);
使用createRadialGradient(x1, y1, r1, x2, y2, r2)
创建一个径向渐变。参数定义了内外圆的位置和半径。
接下去我希望区分填充渐变和描边渐变,并且把这些代码写在一个函数里。创建drawCircle函数,包括circle和isFill参数,传入一个圆,如果isFill为true则填充,否则描边。
function drawCircle(circle, isFill) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
// 创建径向渐变
var radialGradient = ctx.createRadialGradient(circle.x, circle.y, 0, circle.x, circle.y, circle.radius);
radialGradient.addColorStop("0", "red");
radialGradient.addColorStop("0.5", "yellow");
radialGradient.addColorStop("1", "green");
if (isFill) {
// 应用渐变填充
ctx.fillStyle = radialGradient;
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
ctx.fill();
} else {
// 应用渐变描边
ctx.strokeStyle = radialGradient;
ctx.lineWidth = 10; // 可以根据需要调整线宽
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
ctx.stroke();
}
}
// 圆的参数
var circle = {
x: 100,
y: 100,
radius: 50
};
// 画一个填充的圆
drawCircle(circle, true);
// 画一个描边的圆
// drawCircle(circle, false);
不过,这个代码只实现了径向渐变,有兴趣可以在这里停下来自己实现一下填充渐变。完成的代码如下:
function drawCircle(circle, isFill, gradientType) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
// 根据渐变类型创建渐变
var gradient;
if (gradientType === 'linear') {
// 创建线性渐变
gradient = ctx.createLinearGradient(circle.x, circle.y - circle.radius, circle.x + circle.radius, circle.y + circle.radius);
gradient.addColorStop("0", "red");
gradient.addColorStop("1", "green");
} else if (gradientType === 'radial') {
// 创建径向渐变
gradient = ctx.createRadialGradient(circle.x, circle.y, 0, circle.x, circle.y, circle.radius);
gradient.addColorStop("0", "red");
gradient.addColorStop("0.5", "yellow");
gradient.addColorStop("1", "green");
}
if (isFill) {
// 应用渐变填充
ctx.fillStyle = gradient;
} else {
// 应用渐变描边
ctx.strokeStyle = gradient;
ctx.lineWidth = 10; // 可以根据需要调整线宽
}
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
if (isFill) {
ctx.fill();
} else {
ctx.stroke();
}
}
// 圆的参数
var circle = {
x: 100,
y: 100,
radius: 50
};
// 画一个填充的线性渐变圆
drawCircle(circle, true, 'linear');
// 画一个描边的径向渐变圆
// drawCircle(circle, false, 'radial');
拆分函数:
此时我们发现,这个drawCircle函数已经很大了,为了项目的可拓展性和可维护性,我们应该进行一下区分。
function createLinearGradient(ctx, circle) {
return ctx.createLinearGradient(0, 0, circle.x * 2, circle.y * 2);
}
function createRadialGradient(ctx, circle) {
return ctx.createRadialGradient(circle.x, circle.y, 0, circle.x, circle.y, circle.radius);
}
function applyGradient(ctx, gradient, isFill) {
if (isFill) {
ctx.fillStyle = gradient;
} else {
ctx.strokeStyle = gradient;
ctx.lineWidth = 10; // 可以根据需要调整线宽
}
}
function drawCircleWithGradient(ctx, circle, isFill, gradient) {
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
if (isFill) {
ctx.fill();
} else {
ctx.stroke();
}
}
function drawCircle(circle, isFill, gradientType) {
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var gradient;
if (gradientType === 'linear') {
gradient = createLinearGradient(ctx, circle);
gradient.addColorStop("0", "red");
gradient.addColorStop("1", "green");
} else if (gradientType === 'radial') {
gradient = createRadialGradient(ctx, circle);
gradient.addColorStop("0", "red");
gradient.addColorStop("0.5", "yellow");
gradient.addColorStop("1", "green");
}
applyGradient(ctx, gradient, isFill);
drawCircleWithGradient(ctx, circle, isFill, gradient);
}
// 圆的参数
var circle = {
x: 200,
y: 200,
radius: 100
};
// 画一个填充的线性渐变圆
drawCircle(circle, true, 'linear');
// 画一个描边的径向渐变圆
// drawCircle(circle, false, 'radial');
拆分为四个函数:
createLinearGradient: 创建线性渐变。
createRadialGradient: 创建径向渐变。
applyGradient: 应用渐变到填充或描边。
drawCircleWithGradient: 使用给定的渐变绘制圆。
drawCircle 函数: 根据传入的 gradientType 参数调用相应的函数创建渐变。
代码的拓展
上面我们已经初步实现了一个圆的渐变,但是也许还可以传入正方形?矩形?三角形?甚至是更多的类型?
首先需要来实现这几种类型:
// 绘制圆形
function drawCircle(ctx, x, y, radius, isFill) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
}
// 绘制矩形
function drawRect(ctx, x, y, width, height, isFill) {
ctx.beginPath();
ctx.rect(x, y, width, height);
ctx.fill();
ctx.stroke();
}
// 绘制三角形
function drawTriangle(ctx, x1, y1, x2, y2, x3, y3, isFill) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
绘制的时候,就要根据形状来选择分支。我们创建函数drawShape:
// 绘制形状
function drawShape(ctx, shape, isFill) {
let gradient;
switch (shape.type) {
case 'circle':
gradient = createRadialGradient(ctx, shape.x, shape.y, shape.radius);
applyGradient(ctx, gradient, isFill);
drawCircle(ctx, shape.x, shape.y, shape.radius, isFill);
break;
case 'rect':
gradient = createLinearGradient(ctx, 0, 0, shape.width, shape.height);
applyGradient(ctx, gradient, isFill);
drawRect(ctx, shape.x, shape.y, shape.width, shape.height, isFill);
break;
case 'triangle':
gradient = createLinearGradient(ctx, shape.x1, shape.y1, shape.x3, shape.y3);
applyGradient(ctx, gradient, isFill);
drawTriangle(ctx, shape.x1, shape.y1, shape.x2, shape.y2, shape.x3, shape.y3, isFill);
break;
// 可以添加更多形状
}
}
布豪!发现圆形的渐变似乎有问题!我们来修改一下,并且我们发现js文件已经太大了,我们来拆分一下,下面给出一个完整案例:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Canvas 绘图示例</title>
<!-- 引用外部CSS -->
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<canvas id="myCanvas" width="600" height="600">
您的浏览器不支持 HTML5 Canvas 标签。
</canvas>
</div>
<!-- 引用外部JavaScript文件 -->
<script src="js/gradients.js"></script>
<script src="js/shapes.js"></script>
<script src="js/main.js"></script>
</body>
</html>
/* styles.css */
body {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #f0f0f0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
canvas {
border: 2px solid #000;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
// gradients.js
// 创建线性渐变
function createLinearGradient(ctx, x1, y1, x2, y2) {
const gradient = ctx.createLinearGradient(x1, y1, x2, y2);
gradient.addColorStop(0, "red");
gradient.addColorStop(1, "green");
return gradient;
}
// 创建径向渐变
function createRadialGradient(ctx, x, y, r) {
const gradient = ctx.createRadialGradient(x, y, 0, x, y, r);
gradient.addColorStop(0, "red");
gradient.addColorStop(0.5, "yellow");
gradient.addColorStop(1, "green");
return gradient;
}
// shapes.js
// 应用渐变
function applyGradient(ctx, gradient, isFill) {
if (isFill) {
ctx.fillStyle = gradient;
} else {
ctx.strokeStyle = gradient;
ctx.lineWidth = 5; // 可以根据需要调整线宽
}
}
// 绘制圆形
function drawCircle(ctx, x, y, radius, isFill) {
ctx.beginPath();
ctx.arc(x, y, radius, 0, Math.PI * 2);
if (isFill) {
ctx.fill();
} else {
ctx.stroke();
}
}
// 绘制矩形
function drawRect(ctx, x, y, width, height, isFill) {
ctx.beginPath();
ctx.rect(x, y, width, height);
if (isFill) {
ctx.fill();
} else {
ctx.stroke();
}
}
// 绘制三角形
function drawTriangle(ctx, x1, y1, x2, y2, x3, y3, isFill) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.closePath();
if (isFill) {
ctx.fill();
} else {
ctx.stroke();
}
}
// 绘制形状
function drawShape(ctx, shape, isFill) {
let gradient;
switch (shape.type) {
case 'circle':
gradient = createRadialGradient(ctx, shape.x, shape.y, shape.radius);
applyGradient(ctx, gradient, isFill);
drawCircle(ctx, shape.x, shape.y, shape.radius, isFill);
break;
case 'rect':
gradient = createLinearGradient(ctx, shape.x, shape.y, shape.x + shape.width, shape.y + shape.height);
applyGradient(ctx, gradient, isFill);
drawRect(ctx, shape.x, shape.y, shape.width, shape.height, isFill);
break;
case 'triangle':
gradient = createLinearGradient(ctx, shape.x1, shape.y1, shape.x3, shape.y3);
applyGradient(ctx, gradient, isFill);
drawTriangle(ctx, shape.x1, shape.y1, shape.x2, shape.y2, shape.x3, shape.y3, isFill);
break;
// 可以添加更多形状
default:
console.warn(`未知的形状类型: ${shape.type}`);
}
}
// main.js
// 确保所有依赖的脚本已经加载
window.addEventListener('DOMContentLoaded', () => {
// 获取Canvas上下文
const canvas = document.getElementById("myCanvas");
if (!canvas.getContext) {
console.error("浏览器不支持 Canvas!");
return;
}
const ctx = canvas.getContext("2d");
// 定义要绘制的形状
const shapes = [
{ type: 'circle', x: 300, y: 300, radius: 100, isFill: true },
{ type: 'rect', x: 150, y: 150, width: 200, height: 100, isFill: false },
{ type: 'triangle', x1: 50, y1: 50, x2: 150, y2: 50, x3: 100, y3: 150, isFill: false }
];
// 绘制所有形状
shapes.forEach(shape => {
drawShape(ctx, shape, shape.isFill);
});
});
这个项目已经初步搭建了一个有意思的canvas页面,当然我希望它更有意思!
main.js文件上的花活
我们只需要修改main.js文件即可,我们使用随机、循环等方式创建各类对象:
// main.js
// 确保所有依赖的脚本已经加载
window.addEventListener('DOMContentLoaded', () => {
// 获取Canvas上下文
const canvas = document.getElementById("myCanvas");
if (!canvas.getContext) {
console.error("浏览器不支持 Canvas!");
return;
}
const ctx = canvas.getContext("2d");
// 随机颜色生成函数
function getRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
// 定义循环生成形状的函数
function generateShapes() {
const shapes = [];
// 生成圆形:随机大小和位置,大小范围拉大
for (let i = 0; i < 100; i++) {
const radius = 1 + Math.random() * 30; // 随机大小,30-130
const x = Math.random() * (canvas.width - radius * 2) + radius;
const y = Math.random() * (canvas.height - radius * 2) + radius;
shapes.push({ type: 'circle', x, y, radius, isFill: true, color: getRandomColor() });
}
// 生成矩形:随机旋转,渐变颜色,大小和位置随机
for (let i = 0; i < 30; i++) {
const width = 60 + Math.random() * 40;
const height = 40 + Math.random() * 30;
const x = Math.random() * (canvas.width - width);
const y = Math.random() * (canvas.height - height);
const angle = Math.random() * Math.PI; // 随机旋转角度
shapes.push({ type: 'rect', x, y, width, height, isFill: false, angle });
}
// 生成三角形:顶点随机,颜色随机
for (let i = 0; i < 20; i++) {
const x1 = Math.random() * canvas.width;
const y1 = Math.random() * canvas.height;
const x2 = x1 + Math.random() * 100;
const y2 = y1 + Math.random() * 100;
const x3 = x1 + Math.random() * 50;
const y3 = y1 - Math.random() * 100;
shapes.push({ type: 'triangle', x1, y1, x2, y2, x3, y3, isFill: true, color: getRandomColor() });
}
return shapes;
}
// 获取生成的形状
const shapes = generateShapes();
// 绘制所有形状
shapes.forEach(shape => {
// 如果是矩形,先旋转后绘制
if (shape.type === 'rect') {
ctx.save();
ctx.translate(shape.x + shape.width / 2, shape.y + shape.height / 2);
ctx.rotate(shape.angle);
ctx.translate(-(shape.x + shape.width / 2), -(shape.y + shape.height / 2));
drawShape(ctx, shape, shape.isFill);
ctx.restore();
} else if (shape.type === 'triangle') {
ctx.fillStyle = shape.color; // 使用随机颜色填充三角形
drawShape(ctx, shape, shape.isFill);
} else if (shape.type === 'circle') {
ctx.fillStyle = shape.color; // 使用随机颜色填充圆形
drawShape(ctx, shape, shape.isFill);
}
});
});
当然,这个项目看上去依然存在问题,还可以进一步拓展等,更多奇妙的故事等你自己去探索吧~