canvas进阶之特殊形状绘制(在canvas中绘制图像,用哪种方式可以绘制图像的轮廓?)
前言
在阅读完 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();
作者:xfz
链接:https://juejin.cn/post/7031586008282234888