1、甚么是Canvas?
HTML5 提供更多Canvas API,其实质上是两个DOM原素,能看作是应用程序提供更多几块画笔供他们在下面绘图2D或是3D绘图。虽然3D绘出语句(webgl)现阶段在许多应用程序上相容性极差,因此他们一般用作绘出2D绘图。
2、为甚么采用Canvas?
canvas是HTML5导入的条码,在此之后他们一般而言会采用SVG来绘出一些绘图,那么二者之间有甚么差别呢?SVG可翻转向量绘图(Scalable Vector Graphics)是如前所述可扩充记号词汇XML叙述的2D绘图的词汇,二者部分差别:
SVG 影像是采用各种原素建立的,这些原素分别应用作向量影像的结构、绘出与产业布局;而Canvas这类并不叙述影像,而要透过Javascript顺利完成绘出;如前所述,SVG这类是DOM原素,每两个叙述原素也是DOM原素,应用程序在展开绘图时须要展开大量排序以处置每两个原素;而在绘图Canvas的过程中,应用程序只须要绘图两张画笔,余下的是透过Javascript发动机继续执行方法论来绘出;SVG(向量图)不倚赖解析度,弱化不能杂讯;而Canvas(图形)倚赖解析度,弱化会杂讯;虽然Canvas是透过Javascript来顺利完成绘出的,因此移动性极强,他们能比较准确的控制绘图绘图的每一格;从再者而言,如果在高频绘图上要处置过多的DOM原素就意味著操控性一定不能太好,绘图速率会上升许多。Canvas的高效能能够保证繁杂情景中绘图的绘图工作效率,因此现阶段许多应用领域单厢采用Canvas,比如动画电影、格斗游戏绘图、数据建模、相片处置和动态音频处置等。
3、Canvas的基本采用
anvas原素的提及而后透过getContext()
const canvas = document.getElementById(canvas)const ctx = canvas.getContext(2d)
CanvasRenderingContext2DUSB上的绘图API了,接下来他们能了解一些比较常规的采用。
3.1、画笔属性:
width、height:画笔的宽度以及高度,默认大小为300×150;fillStyle:填充绘图的样式,值能是color string、CanvasGradient对象;strokeStyle:轮廓绘图的样式,值能是color string、CanvasGradient对象;lineWidth:绘出线条的宽度;globalAlpha:画笔的透明度,0-1的偏移值;globalCompositeOperation:画笔中新老绘图重叠时的绘图方式,默认为source-over,新绘图覆盖老绘图;ctx.width = 300ctx.height = 300ctx.fillStyle = #fffctx.strokeStyle = bluectx.lineWidth = 5ctx.globalAlpha = 0.3ctx.globalCompositeOperation = destination-out // 新老绘图重叠部分变透明3.2、绘出绘图:
.fillRect(x,y,width,height):绘出两个填充的矩形,矩形左上角的坐标为(x,y),高宽分别为width、height;.strokeRect(x,y,width,height):绘出两个矩形边框,矩形左上角的坐标为(x,y),高宽分别为width、height;.clearRect(x,y,width,height):清除指定矩形区域,让清除部分完全透明;ctx.fillStyle = redctx.fillRect(100,100,100,100)ctx.strokeStyle = bluectx.strokeRect(200,200,100,100)ctx.clearRect(125,125,50,50)ctx.strokeRect(130,130,40,40)3.3、绘出路径:
.beginPath():开始一段路径的绘出;.closePath():从起始点到当前点,结束路径的绘出,非必需;.fill():根据路径生成填充绘图;.stroke():透过路径生成轮廓绘图;.moveTo(x,y):声明一段路径的起始点;.lineTo(x,y):绘出一条从当前坐标到(x,y)的线;ctx.beginPath()ctx.moveTo(50,50)ctx.lineTo(100,100)ctx.lineTo(100,0)ctx.fill()ctx.beginPath()ctx.moveTo(110,100)ctx.lineTo(150,100)ctx.lineTo(150,200)ctx.lineTo(110,200)ctx.closePath() // 轮廓绘图不能根据从当前坐标到起始坐标生成轮廓,因此须要闭合路径ctx.stroke()3.4、绘出圆弧:
.arc(x,y,radius,startAngle,endAngle,anticlockwise):画两个以(x,y)为圆心的以 radius 为半径的圆弧(圆),从 startAngle 开始到 endAngle 结束,按照 anticlockwise 给定的方向(默认为顺时针,false)来生成;arcTo(x1,y1,x2,y2,radius):根据给定的两条切线中的一组切点坐标生成半径为radius的圆弧;注意:arc函数中的角度的单位是弧度而不是度,弧度=(Math.PI/180)*度
// 圆左上部分ctx.beginPath()ctx.arc(100,100,50,Math.PI,Math.PI*3/2,false)ctx.strokeStyle = #ff6700ctx.stroke()// 圆右上部分ctx.beginPath()ctx.arc(100,100,50,Math.PI*3/2,0,false)ctx.strokeStyle = #6700ffctx.stroke()// 圆右下部分ctx.beginPath()ctx.arc(100,100,50,0,Math.PI/2,false)ctx.strokeStyle = #00FFFFctx.stroke()// 圆左下部分ctx.beginPath()ctx.arc(100,100,50,Math.PI/2,Math.PI,false)ctx.strokeStyle = #8B008Bctx.stroke()// 两条切线的交点坐标为(0,0)ctx.beginPath()ctx.moveTo(100,0)ctx.arcTo(0,0,0,100,100)ctx.fillStyle = bluectx.fill()
3.5、渐变对象:
.createLinearGradient(x1, y1, x2, y2):建立两个沿参数坐标指定的直线的渐变,开始坐标为(x1,y1),结束坐标为(x2,y2);.createRadialGradient(x1, y1, r1, x2, y2, r2):建立根据参数确定两个圆的坐标的放射性渐变,开始圆形圆心为(x1,y1),半径为r1;结束圆形圆心为(x2,y2),半径为r2;建立好渐变对象之后,能透过渐变对象上的.addColorStop(offset,color)为每两个渐变阶段填充颜色,offset为0-1的偏移值。
const gradient = ctx.createLinearGradient(50, 50, 250, 50)gradient.addColorStop(0, blue)gradient.addColorStop(0.5, green)gradient.addColorStop(1, red)ctx.fillStyle = gradientctx.fillRect(0, 0, 300, 90)const radialGradient = ctx.createRadialGradient(200,200,100,200,200,50);radialGradient.addColorStop(0,”yellow”);radialGradient.addColorStop(1,”green”);ctx.fillStyle = radialGradient;ctx.fillRect(100,100,200,200);
3.6、像素操作:
.drawImage(image,x,y,width,height):image能是image对象、canvas原素、vilet mousedown = false;function getRandom() {return Math.round(255 * Math.random());function getColor() {return `rgb(${getRandom()},${getRandom()},${getRandom()})`;const gradient = ctx.createLinearGradient(0, 0, 300, 300);gradient.addColorStop(0, getColor());gradient.addColorStop(0.6, getColor());gradient.addColorStop(1, getColor());function clear() {ctx.fillStyle = gradient;ctx.fillRect(0, 0, canvas.width, canvas.height);ctx.beginPath();ctx.fillStyle = gradient;ctx.fillRect(0, 0, 300, 300);function selector(x = 150, y = 150) {clear();ctx.beginPath();ctx.arc(x, y, 5, 0, Math.PI * 2);ctx.strokeSst color = `rgba(${data[0]},${data[1]},${data[2]},${data[3] / 255})`div.innerText = `color: ${color}`;div.style.backgroundColor = colorfunction handleSelector(e) {const x = e.offsetX;const y = e.offsetY;selector(x, y);canvas.addEventListener(“mousedown”, (e) => {mousedown = true;handleSelector(e)canvas.addEventListener(“mouseup”, () => {mousedown = false;canvas.addEventListener(“mousemove”, (e) => {if (mousedown) {handleSelector(e)selector();3.7、画笔状态:
.save():将当前画笔的状态推入到栈中,比如fillStyle、2D转换等;.restore():将栈顶原素弹出,恢复上一次推入栈中画笔的状态;当他们须要透过空间转换来绘出绘图时,保存与恢复画笔的状态是很关键的,因为他们是在同几块画笔上绘出绘图,而变换都是如前所述画笔的,这与他们平时采用到的CSS 2D转换截然不同,因此他们在下一步绘出时要确认此时画笔的状态是否是他们的理想状态。
ctx.save() // 保存画笔初始状态ctx.translate(100,100) // 将画笔原点转移至(100,100)ctx.fillStyle = redctx.fillRect(0,0,50,50)ctx.restore() // 恢复画笔状态,此时画笔原点为(0,0)ctx.fillStyle = bluectx.fillRect(0,0,50,50)
3.8、几何变化:
.translate(x,y):画笔默认的原点是(0,0),此方法能切换原点到(x,y)而不须要手动更改绘出绘图的坐标;.rotate(angle):将画笔旋转一定的角度,angle单位为弧度;.scale(sx,sy):sx为水平方向的翻转比例,sy为竖直方向的翻转比例;.transform(a,b,c,d,e,f):依次为水平翻转、垂直倾斜、水平倾斜、垂直翻转、水平移动、垂直移动;const colors = [red,orange,yellow,green,blue,purple];ctx.translate(150,150)for(let i = 0; i < 6; i++) {ctx.beginPath()ctx.fillStyle = colors[i]ctx.moveTo(0,0)ctx.lineTo(100,0)ctx.lineTo(100,50)ctx.rotate(Math.PI/3)ctx.fill()
4、综合两栖作战
const p = Math.PI;function clock() {const date = new Date();const hour = date.getHours()const s = date.getSeconds();const m = date.getMinutes();const h = !!(hour % 12) ? hour % 12 : 12;ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.save(); // 保存画笔初始状态ctx.translate(150, 150);ctx.rotate(-p / 2);// 轮廓ctx.beginPath();ctx.lineWidth = 5;ctx.strokeStyle = “#76b2ff”;ctx.arc(0, 0, 80, 0, p * 2);ctx.stroke();// 圆心ctx.beginPath();ctx.arc(0, 0, 2, 0, p * 2);ctx.fill();// 分针、秒针刻度for (let i = 0; i < 60; i++) {ctx.beginPath();ctx.rotate(p / 30);ctx.moveTo(75, 0);ctx.lineWidth = 4;ctx.strokeStyle = “#89f086”;ctx.lineTo(80, 0);ctx.stroke();// 时针刻度for (let i = 0; i < 12; i++) {ctx.beginPath()ctx.rotate(p / 6)ctx.moveTo(70, 0)ctx.lineTo(80, 0)ctx.stroke()ctx.save(); // 保存画笔变换之后的状态// 秒针ctx.beginPath();ctx.rotate(s * (p / 30));ctx.lineWidth = 2ctx.strokeStyle = #ff6700ctx.moveTo(0, 0);ctx.lineTo(80, 0);ctx.stroke();// 恢复之前的状态再保存,时针、分针、秒针都是如前所述原点以及画笔方向变换后绘出ctx.restore();ctx.save();// 分针ctx.beginPath();ctx.rotate(m * (p / 30));ctx.lineWidth = 3;ctx.strokeStyle = #6700ffctx.moveTo(0, 0);ctx.lineTo(70, 0);ctx.stroke();ctx.restore();// 时针ctx.beginPath();ctx.rotate(h * (p / 6));ctx.lineWidth = 4;ctx.moveTo(0, 0);ctx.lineTo(60, 0);ctx.stroke();ctx.restore(); // 恢复画笔最初状态document.querySelector(div).innerText = `Now:${h} : ${m} : ${s} ${hour > 12 ? pm : am}`window.requestAnimationFrame(clock);clock();
5、小结
随着互联网的高速发展,用户对页面的视觉和交互有着越来越高的要求,传统的web开发无法得到满足,利用Canvas强大的绘图能力,能让网页显示的内容更加的丰富多彩,也能给用户带来更好的视觉体验。
作者:LLS-FE团队
出处:https://mp.weixin.qq.com/s/bvkx3wOeMvIUU64cktX6iA