共计 6025 个字符,预计需要花费 16 分钟才能阅读完成。
海上生明月,天涯共此时.
—-《望月怀远》(唐·张九龄)
不觉间又到了中秋季节,置信宽广敌人们都收到了公司或其余敌人送来的月饼,我置信没有人不期待中秋(假期)的降临。
“小饼如嚼月, 中有酥与饴”说的就是美味的月饼,既然月饼如此深得大家的青睐,明天咱们就用 canvas 画一个月饼,送个显示器前的你。
这次应用的技术栈是 vue + vite 进行开发编辑工作的,(就是简略的 html+css)这么简略的性能当然应该应用动态 html 的啦,请各 位敌人怎么不便怎么来吧~
目录构造
开始绘制
(1) 第一步绘制一个富裕中国元素的背景(有点密集恐惧症)
canvas {
width: 100%;
height: 100%;
background-image: repeating-radial-gradient(circle at center center,
transparent 0px,
transparent 8px,
rgba(255, 255, 255, 0.05) 8px,
rgba(255, 255, 255, 0.05) 11px,
transparent 11px,
transparent 17px,
rgba(255, 255, 255, 0.05) 17px,
rgba(255, 255, 255, 0.05) 25px,
transparent 25px,
transparent 38px,
rgba(255, 255, 255, 0.05) 38px,
rgba(255, 255, 255, 0.05) 42px),
repeating-radial-gradient(circle at center center,
rgb(230, 0, 0) 0px,
rgb(230, 0, 0) 11px,
rgb(230, 0, 0) 11px,
rgb(230, 0, 0) 19px,
rgb(230, 0, 0) 19px,
rgb(230, 0, 0) 24px,
rgb(230, 0, 0) 24px,
rgb(230, 0, 0) 33px,
rgb(230, 0, 0) 33px,
rgb(230, 0, 0) 44px,
rgb(230, 0, 0) 44px,
rgb(230, 0, 0) 46px);
background-size: 30px 30px;
cursor: pointer;
}
绘制月饼线条
接下来进入到绘制月饼的正式环节,首先咱们要获取到画布,而后再绘制线条,月饼大家都晓得,大部分的花纹是轴对称的展现,所以咱们将其抽出,分为独自的一个个类去进行实现,最初再在底部增加咱们的中秋祝愿或诗句。
constructor(options) {
this.x = 0; // x 轴坐标
this.y = 0; // y 轴坐标
this.name = “ 五仁 ” // 馅名
this.strokeStyle = “rgb(180,110,48)”; // 线条色
this.fillStyle = “rgb(251,216,96)”; // 填充色
this.fontSize = 36; // 字体大小
this.scale = 1; // 缩放大小
Object.assign(this, options)
this.ctx = null;
this.progress = 0; // 绘制进度
this.stepFn = [] // 绘制步骤
this.isComplete = false; // 是否绘制完结
this.nowDate = new Date(); // 以后工夫
this.lastDate = new Date(); // 完结工夫
return this;
}
render(ctx) {
// 渲染
if (!ctx)
throw new Error(“context is undefined.”);
this.ctx = ctx;
this.stepFn.length = 0;
this.stepFn.push(() => this.drawEdge(180, 20))
this.stepFn.push(() => this.drawEdge(140, 12))
this.stepFn.push(() => this.drawRoundRectPath(140, 220, 40))
this.stepFn.push(() => this.drawRoundRectPath(220, 140, 40))
this.stepFn.push(() => this.drawLine(30, -110, 30, 110))
this.stepFn.push(() => this.drawLine(0, -110, 0, 110))
this.stepFn.push(() => this.drawLine(-30, -110, -30, 110))
this.stepFn.push(() => this.drawLine(-110, -30, 110, -30))
this.stepFn.push(() => this.drawLine(-110, 0, 110, 0))
this.stepFn.push(() => this.drawLine(-110, 30, 110, 30))
this.stepFn.push(() => this.drawRect(140, 140))
this.stepFn.push(() => this.drawBox(140))
this.stepFn.push(() => this.drawText())
return this;
}
draw() {
// 绘制
for (let i = 0; i < this.progress; i++) {
this.stepFn[i] && this.stepFn[i]()
}
if (this.progress > this.stepFn.length) return this.isComplete = true;
this.nowDate = new Date();
if(this.nowDate-this.lastDate>200){
this.progress++;
this.lastDate = this.nowDate;
}
}
drawBox(size) {
// 绘制折线盒子
const {ctx, x, y, strokeStyle, scale} = this;
let v = 17,
n = -size / 2;
ctx.save()
ctx.translate(x, y);
ctx.scale(scale, scale)
ctx.beginPath();
ctx.lineCap = “round”;
ctx.lineWidth = 4;
ctx.strokeStyle = strokeStyle;
ctx.moveTo(v + n, n)
ctx.lineTo(v + n, size – v + n)
ctx.lineTo(size – v + n, size – v + n)
ctx.lineTo(size – v + n, v + n)
ctx.lineTo(v * 2 + n, v + n)
ctx.lineTo(v 2 + n, size – v 2 + n)
ctx.lineTo(size – v 2 + n, size – v 2 + n)
ctx.lineTo(size – v * 2 + n, 45 + n)
ctx.stroke()
ctx.restore();
}
drawLine(x1, y1, x2, y2) {
// 绘制线条
const {ctx, x, y, strokeStyle, scale} = this;
ctx.save()
ctx.translate(x, y);
ctx.scale(scale, scale)
ctx.beginPath();
ctx.lineCap = “round”;
ctx.lineWidth = 4;
ctx.strokeStyle = strokeStyle;
ctx.moveTo(x1, y1)
ctx.lineTo(x2, y2)
ctx.stroke()
ctx.restore();
}
drawRect(width, height) {
// 绘制矩形
const {ctx, x, y, strokeStyle, fillStyle, scale} = this;
ctx.save()
ctx.translate(x, y);
ctx.scale(scale, scale)
ctx.beginPath();
ctx.lineCap = “round”;
ctx.lineWidth = 4;
ctx.strokeStyle = strokeStyle;
ctx.fillStyle = fillStyle;
ctx.rect(-width / 2, -height / 2, width, width)
ctx.fill();
ctx.stroke()
ctx.restore();
}
drawRoundRectPath(width, height, radius) {
// 绘制圆角矩形
const {ctx, x, y, strokeStyle, fillStyle, scale} = this;
let w = -width / 2,
h = -height / 2
ctx.save()
ctx.translate(x, y);
ctx.scale(scale, scale)
ctx.lineCap = “round”;
ctx.strokeStyle = strokeStyle;
ctx.fillStyle = fillStyle;
ctx.lineWidth = 5;
ctx.beginPath();
ctx.arc(width – radius + w, height – radius + h, radius, 0, Math.PI / 2);
ctx.lineTo(radius + w, height + h);
ctx.arc(radius + w, height – radius + h, radius, Math.PI / 2, Math.PI);
ctx.lineTo(w, radius + h);
ctx.arc(radius + w, radius + h, radius, Math.PI, Math.PI * 3 / 2);
ctx.lineTo(width – radius + w, h);
ctx.arc(width – radius + w, radius + h, radius, Math.PI 3 / 2, Math.PI 2);
ctx.lineTo(width + w, height – radius + h);
ctx.closePath();
ctx.stroke()
ctx.restore();
}
drawEdge(radius, lineWidth) {
// 绘制花边
const {ctx, x, y, strokeStyle, fillStyle, scale} = this;
let n = 12,
v = 360 / n,
m = 30;
ctx.save()
ctx.translate(x, y);
ctx.scale(scale, scale)
ctx.beginPath();
ctx.lineCap = “round”;
for (let i = 0; i < n; i++) {
let angle1 = i v Math.PI / 180;
let angle2 = (i + 1) v Math.PI / 180;
let angle3 = (i + 0.5) v Math.PI / 180;
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeStyle;
ctx.fillStyle = fillStyle;
let _sx = radius * Math.cos(angle1),
_sy = radius * Math.sin(angle1);
ctx.lineTo(_sx, _sy);
let _ex = radius * Math.cos(angle2),
_ey = radius * Math.sin(angle2);
let _mx = (radius + m) * Math.cos(angle3),
_my = (radius + m) * Math.sin(angle3);
ctx.bezierCurveTo(_mx, _my, _ex, _ey, _ex, _ey)
}
ctx.closePath();
ctx.stroke()
ctx.fill();
ctx.restore();
}
填充文字
drawText(n) {
// 绘制文字
const {ctx, x, y, name, fontSize, strokeStyle, scale} = this;
let size = fontSize;
ctx.save()
ctx.translate(x, y);
ctx.scale(scale, scale)
ctx.fillStyle = strokeStyle;
ctx.textAlign = “center”;
ctx.font = bolder ${size}px fangsong,self
ctx.shadowColor = strokeStyle;
ctx.shadowBlur = 1;
if (name.length == 2) {
ctx.fillText(name.charAt(0), 0, -size * 0.5 + 5);
ctx.fillText(name.charAt(1), 0, size * 0.5 + 5);
}
if (name.length >= 3) {
size *= 0.7;
ctx.font = bolder ${size}px fangsong,self
ctx.fillText(name.charAt(0), 0, -size * 1 + 2);
ctx.fillText(name.charAt(1), 0, size * 0 + 2);
ctx.fillText(name.charAt(2), 0, size * 1 + 2);
}
ctx.restore();
}
增加诗句
应用 canvas 的 filltext 进行对应诗句的填充
drawText() {
const {ctx, w, h, text} = this;
ctx.save();
ctx.fillStyle = “rgb(253,190,0)”;
ctx.textAlign = “center”;
ctx.font = bolder 32px fangsong,self
;
ctx.shadowColor = “rgb(253,190,0)”;
ctx.shadowBlur = 10;
ctx.fillText(text[0].substr(0, this.textIndex), w / 2, h * 0.36 + 240);
if (text[0].length < this.textIndex) {
ctx.fillText(
text[1].substr(0, this.textIndex – text[0].length),
w / 2,
h * 0.36 + 240 + 52
);
}
ctx.restore();
},
切换不同口味
<div class=”taste”>
<span
v-for=”(item, index) in types”
:key=”index”
class=”wur”
@click=”constructor(item)”
{{item}}</span
</div>
data 中定义口味
types: [“ 黑芝麻 ”, “ 五仁 ”, “ 蛋黄 ”, “ 莲蓉 ”, “ 豆沙 ”],
完结
这次使用到的一些图形绘制心愿各位小伙伴可能灵活运用,触类旁通,在中秋行将到来之际,送给敌人、共事一个不一样的中秋月饼吧~
点赞反对、手留余香、与有荣焉,动动你发财的小手哟,感激各位大佬能留下您的脚印。
如果你感觉这篇文章对你有点用的话,麻烦请给咱们的开源我的项目点点 star: http://github.crmeb.net/u/defu 不胜感激!