我正在参加「初夏创意投稿大赛」详情请看:初夏创意投稿大赛
前言
绿竹含新粉,红莲落故衣。夏季到了,大家可以一起约上小伙伴去赏荷花啦。但是被封印在上海我,那就只能想想。不过没事,连对象没有都能new一个的这种事情,荷花还不是自己画一个么?😄偶得灵感,用canvas 画一个吧。
知识点
- 贝塞尔曲线的使用
- canvas如何得到一个高清的图像: 画布大小(400/400) 设置css样式显示为(200/200),这样就是高清的啦,原理和照片一样,放大就是像素,缩小就是高清的
画花瓣
我们首先实现一片花瓣然后在一步一步的实现另外的花瓣,之后组合形成一朵完成的荷花
创建画布
首先创建画布, 我使用的是react(ts)搭建的项目所以代码的话,带有一些类型的定义
// html中或者组件中
<canvas id='lotus-canvas-container' width='600px' height='400px' ></canvas>
// 获取到画布的对象
const lotus:any = document.getElementById('lotus-canvas-container')
const ctx = lotus.getContext('2d')
画花瓣的轮廓
开始画花瓣的一个大概的轮廓, 我抽离了一个单个函数进行绘画
- 花瓣是曲线的,所以用三次贝塞尔曲线
bezierCurveTo
进行画曲线,进行连接 - 其次,花瓣的轮廓边框也是有颜色的,而且还是渐变色,所以我们定义了一个线性渐变
createLinearGradient
的颜色对画的线进行设置
const lotusFun = (ctx:any) => {
// 线的颜色:线性渐变
const grd=ctx.createLinearGradient(300, 100, 400, 200);
grd.addColorStop(0, "rgb(221,172,222)");
grd.addColorStop(1, "white");
// 绘制花瓣
ctx.beginPath();
ctx.moveTo(300, 50);
// 三次贝塞尔曲线
ctx.bezierCurveTo(300, 70, 220, 150, 300, 250);
ctx.bezierCurveTo(320, 280, 400, 300, 430, 230);
ctx.bezierCurveTo(440, 50, 320, 75, 300, 50);
ctx.lineWidth = 1 // 线的宽度
ctx.strokeStyle = grd // 线的颜色
ctx.stroke()
ctx.save()
}
因为背景颜色是白色的,荷花的花瓣也有部分是白色的,所以我把背景颜色调整成了绿色,这样方便查看线条,如下图,就得到了一个花瓣的大概。
填充颜色
然后花瓣的颜色是渐变的,这个时候,我们给花瓣填充对应的渐变色,
填充的颜色同样使用的渐变颜色,但是用的是放射状的渐变createRadialGradient
// 线的颜色:线性渐变
const grd=ctx.createLinearGradient(300, 100, 400, 200);
grd.addColorStop(0, "rgb(221,172,222)");
grd.addColorStop(1, "white");
// 填充的颜色:放射状的渐变
const grd2=ctx.createRadialGradient(300, 20, 100, 400, 300, 100);
grd2.addColorStop(0,"#e295c2");
grd2.addColorStop(1, "white");
// 绘制花瓣
ctx.beginPath();
ctx.moveTo(300, 50);
// 三次贝塞尔曲线
ctx.bezierCurveTo(300, 70, 220, 150, 300, 250);
ctx.bezierCurveTo(320, 280, 400, 300, 430, 230);
ctx.bezierCurveTo(440, 50, 320, 75, 300, 50);
ctx.lineWidth = 1 // 线的宽度
ctx.strokeStyle = grd // 线的颜色
ctx.fillStyle= grd2 // 绘制花瓣的背景色 填充颜色
ctx.fill();
ctx.stroke()
ctx.save()
如图:
画花瓣的纹理
花瓣的大致形状就出来了,但是我们还缺少,花瓣上有那种细小的纹理,所以也加上吧。纹理的组成是一条一条的曲线组成,同时也是线性渐变的。
纹理都是从一个点出发的,而且因为花瓣的颜色并不是很均匀的,所以加上阴影来模糊一下颜色。
因为存在很多条,我这边就不写调试的过程了,,所以就直接是封装成了一个数组进行遍历的。
// 线的颜色:线性渐变
const grd=ctx.createLinearGradient(300, 100, 400, 200);
grd.addColorStop(0, "rgb(221,172,222)");
grd.addColorStop(1, "white");
// 填充的颜色:放射状的渐变
const grd2=ctx.createRadialGradient(300, 20, 100, 400, 300, 100);
grd2.addColorStop(0,"#e295c2");
grd2.addColorStop(1, "white");
// 纹理线的颜色
const grd3=ctx.createLinearGradient(300, 50, 400, 250);
grd3.addColorStop(0, "rgb(217,179,208)");
grd3.addColorStop(1, "rgb(255,255,255)");
// 绘制花瓣
ctx.beginPath();
ctx.moveTo(300, 50);
// 三次贝塞尔曲线
ctx.bezierCurveTo(300, 70, 220, 150, 300, 250);
ctx.bezierCurveTo(320, 280, 400, 300, 430, 230);
ctx.bezierCurveTo(440, 50, 320, 75, 300, 50);
ctx.lineWidth = 1 // 线的宽度
ctx.shadowOffsetX = 12; // 线条阴影
ctx.shadowBlur = 2; // 线条阴影
ctx.shadowColor = grd // 线条阴影颜色
ctx.strokeStyle = grd // 线的颜色
ctx.fillStyle= grd2 // 绘制花瓣的背景色 填充颜色
ctx.fill();
ctx.stroke()
const textureNumList: Array<number[]> = [
[300, 60, 230, 150, 310, 260],
[300, 70, 240, 150, 320, 265],
[300, 80, 260, 150, 330, 269],
[300, 90, 290, 150, 340, 270],
[300, 90, 330, 150, 350, 275],
[300, 90, 370, 120, 360, 275],
[310, 100, 400, 110, 375, 275],
[320, 100, 410, 84, 390, 270],
[320, 94, 420, 60, 400, 266],
[320, 90, 420, 40, 418, 248]
]
// 花瓣的纹理
textureNumList.forEach((textureNum:number[]) => {
ctx.beginPath();
ctx.moveTo(300, 50);
ctx.bezierCurveTo(textureNum[0], textureNum[1], textureNum[2], textureNum[3], textureNum[4], textureNum[5]);
ctx.lineWidth = 1 // 线的宽度
ctx.strokeStyle = grd3 // 线的颜色
ctx.stroke()
})
ctx.save()
如图:
画完整的荷花(耗时太久)
然后就是重复枯燥无味的工作, 不断调整,不断画线条。
简单说下,最下层的先画, 然后就是线条的不同的勾勒和上色
2天后。。。。。。。
荷花出来了
画根茎和荷叶(也是费时的工作)
经历了一个小时之后,我放弃了,太难画了,不是线条难画是颜色的深暗还有过度,所以简单画了一个先凑合吧😂😂😂😂, 不要嫌弃
附上代码:
总结
想法是好的,但是勾勒线条还有一些细节,颜色的调整太难了,只能说画出来的型似。