当前位置:首页 > 科技  > 软件

实战三个有趣案例,十分钟入门Canvas

来源: 责编: 时间:2024-04-02 17:24:03 259观看
导读前言大家好,我是林三心,回想起我当年校招的时候啊,多次被面试官问到canvas,但是我却不会,后来一直想找个机会学一下canvas,但是一直没时间。canvas在前端的地位是越来越重要了,为此,我特地写了3个小项目,让你们10分钟就能入门c

前言

大家好,我是林三心,回想起我当年校招的时候啊,多次被面试官问到canvas,但是我却不会,后来一直想找个机会学一下canvas,但是一直没时间。canvas在前端的地位是越来越重要了,为此,我特地写了3个小项目,让你们10分钟就能入门canvas,是的,我的心里没有她,只有你们T9n28资讯网——每日最新资讯28at.com

一、canvas实现时钟转动

实现以下效果,分为几步:T9n28资讯网——每日最新资讯28at.com

1、找到canvas的中心,画出表心,以及表框T9n28资讯网——每日最新资讯28at.com

2、获取当前时间,并根据时间画出时针,分针,秒针,还有刻度T9n28资讯网——每日最新资讯28at.com

3、使用定时器,每过一秒获取新的时间,并重新绘图,达到时钟转动的效果T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

1.1 表心,表框

画表心,表框有两个知识点:T9n28资讯网——每日最新资讯28at.com

1、找到canvas的中心位置T9n28资讯网——每日最新资讯28at.com

2、绘制圆形T9n28资讯网——每日最新资讯28at.com

//html<canvas id="canvas" width="600" height="600"></canvas>// js// 设置中心点,此时300,300变成了坐标的0,0ctx.translate(300, 300)// 画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)ctx.arc(0, 0, 100, 0, 2 * Math.PI)ctx.arc(0, 0, 5, 0, 2 * Math.PI)// 执行画线段的操作strokectx.stroke()

让我们来看看效果,发现了,好像不对啊,我们是想画两个独立的圆线,怎么画出来的两个圆连到一起了:T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

原因是:上面代码画连个圆时,是连着画的,所以画完大圆后,线还没斩断,就接着画小圆,那肯定会大圆小圆连一起,解决办法就是:beginPath,closePathT9n28资讯网——每日最新资讯28at.com

ctx.translate(300, 300) // 设置中心点,此时300,300变成了坐标的0,0// 画大圆+ ctx.beginPath()// 画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)  ctx.arc(0, 0, 100, 0, 2 * Math.PI)  ctx.stroke() // 执行画线段的操作+ ctx.closePath()// 画小圆+ ctx.beginPath()  ctx.arc(0, 0, 5, 0, 2 * Math.PI)  ctx.stroke()+ ctx.closePath()

1.2 时针,分针,秒针

画这三个指针,有两个知识点:T9n28资讯网——每日最新资讯28at.com

1、根据当前时,分,秒去计算角度T9n28资讯网——每日最新资讯28at.com

2、在计算好的角度上去画出时针,分针,秒针 如何根据算好的角度去画线呢,比如算出当前是3点,那么时针就应该以12点为起始点,顺时针旋转2 * Math.PI / 12 * 3 = 90°,分针和秒针也是同样的道理,只不过跟时针不同的是比例问题而已,因为时在表上有12份,而分针和秒针都是60份T9n28资讯网——每日最新资讯28at.com

图片截屏2021-07-19 下午10.07.19.pngT9n28资讯网——每日最新资讯28at.com

这时候又有一个新问题,还是以上面的例子为例,我算出了90°,那我们怎么画出时针呢?我们可以使用moveTo和lineTo去画线段。至于90°,我们只需要将x轴顺时针旋转90°,然后再画出这条线段,那就得到了指定角度的指针了。但是上面说了,是要以12点为起始点,我们的默认x轴确是水平的,所以我们时分秒针算出角度后,每次都要减去90°。可能这有点绕,我们通过下面的图演示一下,还是以上面3点的例子:T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

截屏2021-07-19 下午10.30.23.pngT9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

这样就得出了3点指针的画线角度了。T9n28资讯网——每日最新资讯28at.com

又又又有新问题了,比如现在我画完了时针,然后我想画分针,x轴已经在我画时针的时候偏转了,这时候肯定要让x轴恢复到原来的模样,我们才能继续画分针,否则画出来的分针是不准的。这时候save和restore就派上用场了,save是把ctx当前的状态打包压入栈中,restore是取出栈顶的状态并赋值给ctx,save可多次,但是restore取状态的次数必须等于save次数T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

截屏2021-07-19 下午10.42.06.pngT9n28资讯网——每日最新资讯28at.com

懂得了上面所说,剩下画刻度了,起始刻度的道理跟时分秒针道理一样,只不过刻度是死的,不需要计算,只需要规则画出60个小刻度,和12个大刻度就行T9n28资讯网——每日最新资讯28at.com

const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')ctx.translate(300, 300) // 设置中心点,此时300,300变成了坐标的0,0// 把状态保存起来+ ctx.save()// 画大圆ctx.beginPath()// 画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)ctx.arc(0, 0, 100, 0, 2 * Math.PI)ctx.stroke() // 执行画线段的操作ctx.closePath()// 画小圆ctx.beginPath()ctx.arc(0, 0, 5, 0, 2 * Math.PI)ctx.stroke()ctx.closePath()----- 新加代码  ------// 获取当前 时,分,秒let time = new Date()let hour = time.getHours() % 12let min = time.getMinutes()let sec = time.getSeconds()// 时针ctx.rotate(2 * Math.PI / 12 * hour + 2 * Math.PI / 12 * (min / 60) - Math.PI / 2)ctx.beginPath()// moveTo设置画线起点ctx.moveTo(-10, 0)// lineTo设置画线经过点ctx.lineTo(40, 0)// 设置线宽ctx.lineWidth = 10ctx.stroke()ctx.closePath()// 恢复成上一次save的状态ctx.restore()// 恢复完再保存一次ctx.save()// 分针ctx.rotate(2 * Math.PI / 60 * min + 2 * Math.PI / 60 * (sec / 60) - Math.PI / 2)ctx.beginPath()ctx.moveTo(-10, 0)ctx.lineTo(60, 0)ctx.lineWidth = 5ctx.strokeStyle = 'blue'ctx.stroke()ctx.closePath()ctx.restore()ctx.save()//秒针ctx.rotate(2 * Math.PI / 60 * sec -  - Math.PI / 2)ctx.beginPath()ctx.moveTo(-10, 0)ctx.lineTo(80, 0)ctx.strokeStyle = 'red'ctx.stroke()ctx.closePath()ctx.restore()ctx.save()// 绘制刻度,也是跟绘制时分秒针一样,只不过刻度是死的ctx.lineWidth = 1for (let i = 0; i < 60; i++) {    ctx.rotate(2 * Math.PI / 60)    ctx.beginPath()    ctx.moveTo(90, 0)    ctx.lineTo(100, 0)    // ctx.strokeStyle = 'red'    ctx.stroke()    ctx.closePath()}ctx.restore()ctx.save()ctx.lineWidth = 5for (let i = 0; i < 12; i++) {    ctx.rotate(2 * Math.PI / 12)    ctx.beginPath()    ctx.moveTo(85, 0)    ctx.lineTo(100, 0)    ctx.stroke()    ctx.closePath()}ctx.restore()

图片图片T9n28资讯网——每日最新资讯28at.com

最后一步就是更新视图,使时钟转动起来,第一想到的肯定是定时器setInterval,但是注意一个问题:每次更新视图的时候都要把上一次的画布清除,再开始画新的视图,不然就会出现千手观音的景象T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

附上最终代码:T9n28资讯网——每日最新资讯28at.com

const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')setInterval(() => {    ctx.save()    ctx.clearRect(0, 0, 600, 600)    ctx.translate(300, 300) // 设置中心点,此时300,300变成了坐标的0,0    ctx.save()    // 画大圆    ctx.beginPath()    // 画圆线使用arc(中心点X,中心点Y,半径,起始角度,结束角度)    ctx.arc(0, 0, 100, 0, 2 * Math.PI)    ctx.stroke() // 执行画线段的操作    ctx.closePath()    // 画小圆    ctx.beginPath()    ctx.arc(0, 0, 5, 0, 2 * Math.PI)    ctx.stroke()    ctx.closePath()    // 获取当前 时,分,秒    let time = new Date()    let hour = time.getHours() % 12    let min = time.getMinutes()    let sec = time.getSeconds()    // 时针    ctx.rotate(2 * Math.PI / 12 * hour + 2 * Math.PI / 12 * (min / 60) - Math.PI / 2)    ctx.beginPath()    // moveTo设置画线起点    ctx.moveTo(-10, 0)    // lineTo设置画线经过点    ctx.lineTo(40, 0)    // 设置线宽    ctx.lineWidth = 10    ctx.stroke()    ctx.closePath()    ctx.restore()    ctx.save()    // 分针    ctx.rotate(2 * Math.PI / 60 * min + 2 * Math.PI / 60 * (sec / 60) - Math.PI / 2)    ctx.beginPath()    ctx.moveTo(-10, 0)    ctx.lineTo(60, 0)    ctx.lineWidth = 5    ctx.strokeStyle = 'blue'    ctx.stroke()    ctx.closePath()    ctx.restore()    ctx.save()    //秒针    ctx.rotate(2 * Math.PI / 60 * sec - Math.PI / 2)    ctx.beginPath()    ctx.moveTo(-10, 0)    ctx.lineTo(80, 0)    ctx.strokeStyle = 'red'    ctx.stroke()    ctx.closePath()    ctx.restore()    ctx.save()    // 绘制刻度,也是跟绘制时分秒针一样,只不过刻度是死的    ctx.lineWidth = 1    for (let i = 0; i < 60; i++) {        ctx.rotate(2 * Math.PI / 60)        ctx.beginPath()        ctx.moveTo(90, 0)        ctx.lineTo(100, 0)        // ctx.strokeStyle = 'red'        ctx.stroke()        ctx.closePath()    }    ctx.restore()    ctx.save()    ctx.lineWidth = 5    for (let i = 0; i < 12; i++) {        ctx.rotate(2 * Math.PI / 12)        ctx.beginPath()        ctx.moveTo(85, 0)        ctx.lineTo(100, 0)        // ctx.strokeStyle = 'red'        ctx.stroke()        ctx.closePath()    }    ctx.restore()    ctx.restore()}, 1000)

效果 very good啊:T9n28资讯网——每日最新资讯28at.com

clock的副本clock的副本T9n28资讯网——每日最新资讯28at.com

二、canvas实现刮刮卡

小时候很多人都买过充值卡把,懂的都懂啊哈,用指甲刮开这层灰皮,就能看底下的答案了。T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

思路是这样的:T9n28资讯网——每日最新资讯28at.com

1、底下答案是一个div,顶部灰皮是一个canvas,canvas一开始盖住divT9n28资讯网——每日最新资讯28at.com

2、鼠标事件,点击时并移动时,鼠标经过的路径都画圆形开路,并且设置globalCompositeOperation为destination-out,使鼠标经过的路径都变成透明,一透明,自然就显示出下方的答案信息。T9n28资讯网——每日最新资讯28at.com

关于fill这个方法,其实是对标stroke的,fill是把图形填充,stroke只是画出边框线T9n28资讯网——每日最新资讯28at.com

// html<canvas id="canvas" width="400" height="100"></canvas><div class="text">恭喜您获得100w</div><style>        * {            margin: 0;            padding: 0;        }        .text {            position: absolute;            left: 130px;            top: 35px;            z-index: -1;        }</style>// jsconst canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')// 填充的颜色ctx.fillStyle = 'darkgray'// 填充矩形 fillRect(起始X,起始Y,终点X,终点Y)ctx.fillRect(0, 0, 400, 100)ctx.fillStyle = '#fff'// 绘制填充文字ctx.fillText('刮刮卡', 180, 50)let isDraw = falsecanvas.onmousedown = function () {    isDraw = true}canvas.onmousemove = function (e) {    if (!isDraw) return    // 计算鼠标在canvas里的位置    const x = e.pageX - canvas.offsetLeft    const y = e.pageY - canvas.offsetTop    // 设置globalCompositeOperation    ctx.globalCompositeOperation = 'destination-out'    // 画圆    ctx.arc(x, y, 10, 0, 2 * Math.PI)    // 填充圆形    ctx.fill()}canvas.onmouseup = function () {    isDraw = false}

效果如下:T9n28资讯网——每日最新资讯28at.com

guaguaka.gifguaguaka.gifT9n28资讯网——每日最新资讯28at.com

三、canvas实现画板和保存

框架:使用vue + elementUIT9n28资讯网——每日最新资讯28at.com

其实很简单,难点有以下几点:T9n28资讯网——每日最新资讯28at.com

1、鼠标拖拽画正方形和圆形T9n28资讯网——每日最新资讯28at.com

2、画完一个保存画布,下次再画的时候叠加T9n28资讯网——每日最新资讯28at.com

3、保存图片T9n28资讯网——每日最新资讯28at.com

第一点,只需要计算出鼠标点击的点坐标,以及鼠标的当前坐标,就可以计算了,矩形长宽计算:x - beginX, y - beginY,圆形则要利用勾股定理:Math.sqrt((x - beginX) * (x - beginX) + (y - beginY) * (y - beginY))T9n28资讯网——每日最新资讯28at.com

第二点,则要利用canvas的getImageData和putImageData方法T9n28资讯网——每日最新资讯28at.com

第三点,思路是将canvas生成图片链接,并赋值给具有下载功能的a标签,并主动点击a标签进行图片下载T9n28资讯网——每日最新资讯28at.com

看看效果吧:T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

图片图片T9n28资讯网——每日最新资讯28at.com

具体代码我就不过多讲解了,说难也不难,只要前面两个项目理解了,这个项目很容易就懂了:T9n28资讯网——每日最新资讯28at.com

<template>  <div>    <div style="margin-bottom: 10px; display: flex; align-items: center">      <el-button @click="changeType('huabi')" type="primary">画笔</el-button>      <el-button @click="changeType('rect')" type="success">正方形</el-button>      <el-button        @click="changeType('arc')"        type="warning"        style="margin-right: 10px"        >圆形</el-button      >      <div>颜色:</div>      <el-color-picker v-model="color"></el-color-picker>      <el-button @click="clear">清空</el-button>      <el-button @click="saveImg">保存</el-button>    </div>    <canvas      id="canvas"      width="800"      height="400"      @mousedown="canvasDown"      @mousemove="canvasMove"      @mouseout="canvasUp"      @mouseup="canvasUp"    >    </canvas>  </div></template><script>export default {  data() {    return {      type: "huabi",      isDraw: false,      canvasDom: null,      ctx: null,      beginX: 0,      beginY: 0,      color: "#000",      imageData: null,    };  },  mounted() {    this.canvasDom = document.getElementById("canvas");    this.ctx = this.canvasDom.getContext("2d");  },  methods: {    changeType(type) {      this.type = type;    },    canvasDown(e) {      this.isDraw = true;      const canvas = this.canvasDom;      this.beginX = e.pageX - canvas.offsetLeft;      this.beginY = e.pageY - canvas.offsetTop;    },    canvasMove(e) {      if (!this.isDraw) return;      const canvas = this.canvasDom;      const ctx = this.ctx;      const x = e.pageX - canvas.offsetLeft;      const y = e.pageY - canvas.offsetTop;      this[`${this.type}Fn`](ctx, x, y);    },    canvasUp() {      this.imageData = this.ctx.getImageData(0, 0, 800, 400);      this.isDraw = false;    },    huabiFn(ctx, x, y) {      ctx.beginPath();      ctx.arc(x, y, 5, 0, 2 * Math.PI);      ctx.fillStyle = this.color;      ctx.fill();      ctx.closePath();    },    rectFn(ctx, x, y) {      const beginX = this.beginX;      const beginY = this.beginY;      ctx.clearRect(0, 0, 800, 400);      this.imageData && ctx.putImageData(this.imageData, 0, 0, 0, 0, 800, 400);      ctx.beginPath();      ctx.strokeStyle = this.color;      ctx.rect(beginX, beginY, x - beginX, y - beginY);      ctx.stroke();      ctx.closePath();    },    arcFn(ctx, x, y) {      const beginX = this.beginX;      const beginY = this.beginY;      this.isDraw && ctx.clearRect(0, 0, 800, 400);      this.imageData && ctx.putImageData(this.imageData, 0, 0, 0, 0, 800, 400);      ctx.beginPath();      ctx.strokeStyle = this.color;      ctx.arc(        beginX,        beginY,        Math.round(          Math.sqrt((x - beginX) * (x - beginX) + (y - beginY) * (y - beginY))        ),        0,        2 * Math.PI      );      ctx.stroke();      ctx.closePath();    },    saveImg() {      const url = this.canvasDom.toDataURL();      const a = document.createElement("a");      a.download = "sunshine";      a.href = url;      document.body.appendChild(a);      a.click();      document.body.removeChild(a);    },    clear() {        this.imageData = null        this.ctx.clearRect(0, 0, 800, 400)    }  },};</script><style lang="scss" scoped>#canvas {  border: 1px solid black;}</style>


T9n28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-80899-0.html实战三个有趣案例,十分钟入门Canvas

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 探索全新 JavaScript Server Worker:WinterJS 全面解析

下一篇: Go 语言中怎么使用依赖注入?

标签:
  • 热门焦点
  • K60 Pro官方停产 第三方瞬间涨价

    虽然没有官方宣布,但Redmi的一些高管也已经透露了,Redmi K60 Pro已经停产且不会补货,这一切都是为了即将到来的K60 Ultra铺路,属于厂家的正常操作。但有意思的是该机在停产之后
  • Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • 6月iOS设备性能榜:M2稳居榜首 A系列只能等一手3nm来救

    没有新品发布,自然iOS设备性能榜的上榜设备就没有什么更替,仅仅只有跑分变化而产生的排名变动,毕竟苹果新品的发布节奏就是这样的,一年下来也就几个移动端新品,不会像安卓厂商,一
  • 十个可以手动编写的 JavaScript 数组 API

    JavaScript 中有很多API,使用得当,会很方便,省力不少。 你知道它的原理吗? 今天这篇文章,我们将对它们进行一次小总结。现在开始吧。1.forEach()forEach()用于遍历数组接收一参
  • 服务存储设计模式:Cache-Aside模式

    Cache-Aside模式一种常用的缓存方式,通常是把数据从主存储加载到KV缓存中,加速后续的访问。在存在重复度的场景,Cache-Aside可以提升服务性能,降低底层存储的压力,缺点是缓存和底
  • 2023年,我眼中的字节跳动

    此时此刻(2023年7月),字节跳动从未上市,也从未公布过任何官方的上市计划;但是这并不妨碍它成为中国最受关注的互联网公司之一。从2016-17年的抖音强势崛起,到2018年的&ldquo;头腾
  • 最“俊美”淘宝卖家,靠直播和短视频圈粉,上架秒光,年销3000万

    来源 | 电商在线文|易琬玉编辑|斯问受访店铺:Ringdoll戒之人形图源:微博@御座的黄山、&ldquo;Ringdoll戒之人形&rdquo;淘宝店铺有关外貌的评价,黄山已经听累了。生于1985年的他,哪
  • 造车两年股价跌六成,小米的估值逻辑变了吗?

    如果从小米官宣造车后的首个交易日起持有小米集团的股票,那么截至2023年上半年最后一个交易日,投资者将浮亏59.16%,同区间的恒生科技指数跌幅为52.78%
  • 由于成本持续增加,笔记本产品价格预计将明显上涨

    根据知情人士透露,由于材料、物流等成本持续增加,笔记本产品价格预计将在2021年下半年有明显上涨。进入6月下旬以来,全球半导体芯片缺货情况加剧,显卡、处理器
Top