前言
在阅读完 canvas - 基础了解 中后,想必大家对 canvas 有了一定的认知,那么接下来,咱们就玩点稍微有点意思的东西....
创建弧/曲线
- 创建介于两个切线之间的弧/曲线
var c = document.getElementById('canvas');
var ctx = c.getContext('2d');
ctx.strokeStyle = 'red';
ctx.moveTo(20, 20); // 创建开始点
ctx.arcTo(40, 20, 40, 50, 20); // 创建弧
ctx.stroke(); // 进行绘制
API
-
三次贝塞尔曲线:有两个控制点
- bezierCurveTo(控制点1x,控制点1y,控制点2x,控制点2y,结束点x, 结束点y)
- 二次贝塞尔曲线:它仅有有一个控制点
- 二次贝塞尔曲线:它仅有有一个控制点
bezierCurveTo - demo
//控制点
function draw(id) {
var canvas = document.getElementById(id);
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#eeeeef';
ctx.fillRect(0, 0, 300, 400);
var dx = 150;
var dy = 150;
var s = 100;
ctx.beginPath();
ctx.fillStyle = 'rgb(100,255,100)';
var x = Math.sin(0);
var y = Math.cos(0);
var dig = (Math.PI / 15) * 11;
ctx.moveTo(dx, dy);
for (var i = 0; i < 30; i++) {
x = Math.sin(i * dig);
y = Math.cos(i * dig);
ctx.bezierCurveTo(dx + x * s, dy + y * s - 100, dx + x * s + 100, dy + y * s, dx + x * s, dy + y * s);
}
ctx.closePath();
ctx.fill();
ctx.stroke();
}
bezierCurveTo + quadraticCurveTo - demo
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// 画天空
var g = context.createRadialGradient(
canvas.width / 2,
canvas.height / 2,
0,
canvas.width / 2,
canvas.height,
canvas.height
);
g.addColorStop(0.0, '#035');
g.addColorStop(1.0, 'black');
context.fillStyle = g;
context.fillRect(0, 0, canvas.width, canvas.height);
/****** 画星星 ******/
for (var i = 0; i < 100; i++) {
var R = parseInt(Math.random() * 10);
var x = parseInt(Math.random() * canvas.width) + 5;
var y = parseInt(Math.random() * canvas.height * 0.6) + 5;
var rot = parseInt(Math.random() * 50);
drawStar(context, R, x, y, rot, '#fb3');
}
//半径小,半径大,x,y,线宽,角度,颜色,类型
function drawStar(ctx, R, x, y, rot, color) {
ctx.save();
ctx.translate(x, y);
ctx.scale(R, R);
ctx.rotate((rot / 180) * Math.PI);
starPath(ctx, color);
ctx.restore();
}
function starPath(ctx, color) {
ctx.beginPath();
for (var i = 0; i <= 5; i++) {
ctx.lineTo(Math.cos(((18 + i * 72) / 180) * Math.PI), -Math.sin(((18 + i * 72) / 180) * Math.PI));
ctx.lineTo(Math.cos(((54 + i * 72) / 180) * Math.PI) * 0.5, -Math.sin(((54 + i * 72) / 180) * Math.PI) * 0.5);
ctx.fillStyle = color;
ctx.fill();
}
}
/****** 画月亮 *******/
drawMoon(context, 100, 100, 50);
function drawMoon(ctx, x, y, r) {
ctx.beginPath();
ctx.save();
ctx.translate(x, y);
ctx.scale(r, r);
drawMoonPath(ctx, r);
ctx.restore();
}
function drawMoonPath(ctx) {
// 画半圆
ctx.arc(0, 0, 1, 0.5 * Math.PI, 1.5 * Math.PI, true);
ctx.moveTo(0, -1);
ctx.quadraticCurveTo(1, 0, 0, 1);
ctx.closePath();
ctx.fillStyle = '#fb3';
ctx.fill();
}
/****** 画陆地 *******/
drawLand(context);
function drawLand(ctx) {
ctx.beginPath();
ctx.moveTo(0, canvas.height * 0.8);
ctx.bezierCurveTo(300, canvas.height * 0.6, 600, canvas.height * 0.9, canvas.width, canvas.height * 0.8);
ctx.lineTo(canvas.width, canvas.height);
// ctx.lineTo(0, canvas.height);
var g = ctx.createLinearGradient(0, canvas.height, 0, canvas.height * 0.7);
g.addColorStop(0.0, '#030');
g.addColorStop(1.0, '#580');
ctx.fillStyle = g;
ctx.fill();
}
非零环绕规则
路径一正一反,则求和为0
- canvas在进行填充的时候是否要进行填充的判断依据.用来判断内侧和外侧的方式
- 在判断填充的区域拉一条线出来,拉到图形的外面,这条拉出来的线就是辅助线。
- 判断绘制的线是否是从辅助线的左边穿过到辅助线的右边,此时这种穿过的方式记录为+1
- 如果是从辅助线的右边穿到辅助线的左边,就记做-1.
- 最后将所有记录的数字进行求和,如果求和的结果为0,代表这块区域不要填充,否则,必须填充
剪纸效果
- 本例中,两个圆的路径一顺时针一逆时针,求和为0,不填充
var canvas = document.getElementById('canvas');
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.arc(200, 200, 100, 0, Math.PI * 2);
ctx.arc(200, 200, 50, 0, Math.PI * 2, true);
// ctx.closePath();
ctx.fillStyle = '#058';
ctx.shadowColor = 'gray';
ctx.shadowOffsetX = 10;
ctx.shadowOffsetY = 10;
ctx.shadowBlur = 10;
ctx.fill();