日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

實戰(zhàn)三個有趣案例,十分鐘入門Canvas

來源: 責(zé)編: 時間:2024-04-02 17:24:03 184觀看
導(dǎo)讀前言大家好,我是林三心,回想起我當(dāng)年校招的時候啊,多次被面試官問到canvas,但是我卻不會,后來一直想找個機會學(xué)一下canvas,但是一直沒時間。canvas在前端的地位是越來越重要了,為此,我特地寫了3個小項目,讓你們10分鐘就能入門c

前言

大家好,我是林三心,回想起我當(dāng)年校招的時候啊,多次被面試官問到canvas,但是我卻不會,后來一直想找個機會學(xué)一下canvas,但是一直沒時間。canvas在前端的地位是越來越重要了,為此,我特地寫了3個小項目,讓你們10分鐘就能入門canvas,是的,我的心里沒有她,只有你們Bkx28資訊網(wǎng)——每日最新資訊28at.com

一、canvas實現(xiàn)時鐘轉(zhuǎn)動

實現(xiàn)以下效果,分為幾步:Bkx28資訊網(wǎng)——每日最新資訊28at.com

1、找到canvas的中心,畫出表心,以及表框Bkx28資訊網(wǎng)——每日最新資訊28at.com

2、獲取當(dāng)前時間,并根據(jù)時間畫出時針,分針,秒針,還有刻度Bkx28資訊網(wǎng)——每日最新資訊28at.com

3、使用定時器,每過一秒獲取新的時間,并重新繪圖,達到時鐘轉(zhuǎn)動的效果Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

1.1 表心,表框

畫表心,表框有兩個知識點:Bkx28資訊網(wǎng)——每日最新資訊28at.com

1、找到canvas的中心位置Bkx28資訊網(wǎng)——每日最新資訊28at.com

2、繪制圓形Bkx28資訊網(wǎng)——每日最新資訊28at.com

//html<canvas id="canvas" width="600" height="600"></canvas>// js// 設(shè)置中心點,此時300,300變成了坐標的0,0ctx.translate(300, 300)// 畫圓線使用arc(中心點X,中心點Y,半徑,起始角度,結(jié)束角度)ctx.arc(0, 0, 100, 0, 2 * Math.PI)ctx.arc(0, 0, 5, 0, 2 * Math.PI)// 執(zhí)行畫線段的操作strokectx.stroke()

讓我們來看看效果,發(fā)現(xiàn)了,好像不對啊,我們是想畫兩個獨立的圓線,怎么畫出來的兩個圓連到一起了:Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

原因是:上面代碼畫連個圓時,是連著畫的,所以畫完大圓后,線還沒斬斷,就接著畫小圓,那肯定會大圓小圓連一起,解決辦法就是:beginPath,closePathBkx28資訊網(wǎng)——每日最新資訊28at.com

ctx.translate(300, 300) // 設(shè)置中心點,此時300,300變成了坐標的0,0// 畫大圓+ ctx.beginPath()// 畫圓線使用arc(中心點X,中心點Y,半徑,起始角度,結(jié)束角度)  ctx.arc(0, 0, 100, 0, 2 * Math.PI)  ctx.stroke() // 執(zhí)行畫線段的操作+ ctx.closePath()// 畫小圓+ ctx.beginPath()  ctx.arc(0, 0, 5, 0, 2 * Math.PI)  ctx.stroke()+ ctx.closePath()

1.2 時針,分針,秒針

畫這三個指針,有兩個知識點:Bkx28資訊網(wǎng)——每日最新資訊28at.com

1、根據(jù)當(dāng)前時,分,秒去計算角度Bkx28資訊網(wǎng)——每日最新資訊28at.com

2、在計算好的角度上去畫出時針,分針,秒針 如何根據(jù)算好的角度去畫線呢,比如算出當(dāng)前是3點,那么時針就應(yīng)該以12點為起始點,順時針旋轉(zhuǎn)2 * Math.PI / 12 * 3 = 90°,分針和秒針也是同樣的道理,只不過跟時針不同的是比例問題而已,因為時在表上有12份,而分針和秒針都是60份Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片截屏2021-07-19 下午10.07.19.pngBkx28資訊網(wǎng)——每日最新資訊28at.com

這時候又有一個新問題,還是以上面的例子為例,我算出了90°,那我們怎么畫出時針呢?我們可以使用moveTo和lineTo去畫線段。至于90°,我們只需要將x軸順時針旋轉(zhuǎn)90°,然后再畫出這條線段,那就得到了指定角度的指針了。但是上面說了,是要以12點為起始點,我們的默認x軸確是水平的,所以我們時分秒針算出角度后,每次都要減去90°。可能這有點繞,我們通過下面的圖演示一下,還是以上面3點的例子:Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

截屏2021-07-19 下午10.30.23.pngBkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

這樣就得出了3點指針的畫線角度了。Bkx28資訊網(wǎng)——每日最新資訊28at.com

又又又有新問題了,比如現(xiàn)在我畫完了時針,然后我想畫分針,x軸已經(jīng)在我畫時針的時候偏轉(zhuǎn)了,這時候肯定要讓x軸恢復(fù)到原來的模樣,我們才能繼續(xù)畫分針,否則畫出來的分針是不準的。這時候save和restore就派上用場了,save是把ctx當(dāng)前的狀態(tài)打包壓入棧中,restore是取出棧頂?shù)臓顟B(tài)并賦值給ctx,save可多次,但是restore取狀態(tài)的次數(shù)必須等于save次數(shù)Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

截屏2021-07-19 下午10.42.06.pngBkx28資訊網(wǎng)——每日最新資訊28at.com

懂得了上面所說,剩下畫刻度了,起始刻度的道理跟時分秒針道理一樣,只不過刻度是死的,不需要計算,只需要規(guī)則畫出60個小刻度,和12個大刻度就行Bkx28資訊網(wǎng)——每日最新資訊28at.com

const canvas = document.getElementById('canvas')const ctx = canvas.getContext('2d')ctx.translate(300, 300) // 設(shè)置中心點,此時300,300變成了坐標的0,0// 把狀態(tài)保存起來+ ctx.save()// 畫大圓ctx.beginPath()// 畫圓線使用arc(中心點X,中心點Y,半徑,起始角度,結(jié)束角度)ctx.arc(0, 0, 100, 0, 2 * Math.PI)ctx.stroke() // 執(zhí)行畫線段的操作ctx.closePath()// 畫小圓ctx.beginPath()ctx.arc(0, 0, 5, 0, 2 * Math.PI)ctx.stroke()ctx.closePath()----- 新加代碼  ------// 獲取當(dāng)前 時,分,秒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設(shè)置畫線起點ctx.moveTo(-10, 0)// lineTo設(shè)置畫線經(jīng)過點ctx.lineTo(40, 0)// 設(shè)置線寬ctx.lineWidth = 10ctx.stroke()ctx.closePath()// 恢復(fù)成上一次save的狀態(tài)ctx.restore()// 恢復(fù)完再保存一次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()

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

最后一步就是更新視圖,使時鐘轉(zhuǎn)動起來,第一想到的肯定是定時器setInterval,但是注意一個問題:每次更新視圖的時候都要把上一次的畫布清除,再開始畫新的視圖,不然就會出現(xiàn)千手觀音的景象Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

附上最終代碼:Bkx28資訊網(wǎng)——每日最新資訊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) // 設(shè)置中心點,此時300,300變成了坐標的0,0    ctx.save()    // 畫大圓    ctx.beginPath()    // 畫圓線使用arc(中心點X,中心點Y,半徑,起始角度,結(jié)束角度)    ctx.arc(0, 0, 100, 0, 2 * Math.PI)    ctx.stroke() // 執(zhí)行畫線段的操作    ctx.closePath()    // 畫小圓    ctx.beginPath()    ctx.arc(0, 0, 5, 0, 2 * Math.PI)    ctx.stroke()    ctx.closePath()    // 獲取當(dāng)前 時,分,秒    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設(shè)置畫線起點    ctx.moveTo(-10, 0)    // lineTo設(shè)置畫線經(jīng)過點    ctx.lineTo(40, 0)    // 設(shè)置線寬    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啊:Bkx28資訊網(wǎng)——每日最新資訊28at.com

clock的副本clock的副本Bkx28資訊網(wǎng)——每日最新資訊28at.com

二、canvas實現(xiàn)刮刮卡

小時候很多人都買過充值卡把,懂的都懂啊哈,用指甲刮開這層灰皮,就能看底下的答案了。Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

思路是這樣的:Bkx28資訊網(wǎng)——每日最新資訊28at.com

1、底下答案是一個div,頂部灰皮是一個canvas,canvas一開始蓋住divBkx28資訊網(wǎng)——每日最新資訊28at.com

2、鼠標事件,點擊時并移動時,鼠標經(jīng)過的路徑都畫圓形開路,并且設(shè)置globalCompositeOperation為destination-out,使鼠標經(jīng)過的路徑都變成透明,一透明,自然就顯示出下方的答案信息。Bkx28資訊網(wǎng)——每日最新資訊28at.com

關(guān)于fill這個方法,其實是對標stroke的,fill是把圖形填充,stroke只是畫出邊框線Bkx28資訊網(wǎng)——每日最新資訊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    // 設(shè)置globalCompositeOperation    ctx.globalCompositeOperation = 'destination-out'    // 畫圓    ctx.arc(x, y, 10, 0, 2 * Math.PI)    // 填充圓形    ctx.fill()}canvas.onmouseup = function () {    isDraw = false}

效果如下:Bkx28資訊網(wǎng)——每日最新資訊28at.com

guaguaka.gifguaguaka.gifBkx28資訊網(wǎng)——每日最新資訊28at.com

三、canvas實現(xiàn)畫板和保存

框架:使用vue + elementUIBkx28資訊網(wǎng)——每日最新資訊28at.com

其實很簡單,難點有以下幾點:Bkx28資訊網(wǎng)——每日最新資訊28at.com

1、鼠標拖拽畫正方形和圓形Bkx28資訊網(wǎng)——每日最新資訊28at.com

2、畫完一個保存畫布,下次再畫的時候疊加Bkx28資訊網(wǎng)——每日最新資訊28at.com

3、保存圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

第一點,只需要計算出鼠標點擊的點坐標,以及鼠標的當(dāng)前坐標,就可以計算了,矩形長寬計算:x - beginX, y - beginY,圓形則要利用勾股定理:Math.sqrt((x - beginX) * (x - beginX) + (y - beginY) * (y - beginY))Bkx28資訊網(wǎng)——每日最新資訊28at.com

第二點,則要利用canvas的getImageData和putImageData方法Bkx28資訊網(wǎng)——每日最新資訊28at.com

第三點,思路是將canvas生成圖片鏈接,并賦值給具有下載功能的a標簽,并主動點擊a標簽進行圖片下載Bkx28資訊網(wǎng)——每日最新資訊28at.com

看看效果吧:Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片Bkx28資訊網(wǎng)——每日最新資訊28at.com

具體代碼我就不過多講解了,說難也不難,只要前面兩個項目理解了,這個項目很容易就懂了:Bkx28資訊網(wǎng)——每日最新資訊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>


Bkx28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-80899-0.html實戰(zhàn)三個有趣案例,十分鐘入門Canvas

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

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

下一篇: Go 語言中怎么使用依賴注入?

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 惠东县| 外汇| 南投市| 孟村| 金昌市| 莱阳市| 扎赉特旗| 苏尼特左旗| 辽宁省| 南和县| 长泰县| 岑溪市| 揭西县| 祁阳县| 崇义县| 呼伦贝尔市| 浙江省| 洮南市| 古浪县| 阳山县| 嘉兴市| 赤水市| 诏安县| 荔浦县| 三台县| 岐山县| 龙游县| 灵璧县| 胶南市| 卢氏县| 时尚| 敦煌市| 阳春市| 梁山县| 廊坊市| 历史| 义乌市| 广南县| 伊春市| 和政县| 横峰县|