麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁(yè) > 開發(fā) > HTML5 > 正文

使用html5 canvas繪制圓環(huán)動(dòng)效

2024-09-05 07:22:54
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

最近筆者有個(gè)需求,需求內(nèi)容為:一組文字顯示在圓環(huán)的周圍,用戶可添加文字,文字圍繞著圓環(huán),每個(gè)詞對(duì)應(yīng)圓環(huán)周圍的的藍(lán)色小圓點(diǎn),當(dāng)用戶鼠標(biāo)放在圓環(huán)上方小藍(lán)點(diǎn)時(shí)時(shí),實(shí)現(xiàn)放射出三角形,再顯示出文字,先看看動(dòng)圖效果吧!

​ ​​

如上圖所示,當(dāng)鼠標(biāo)放在對(duì)應(yīng)藍(lán)色小圓點(diǎn)上時(shí),需要放射出射類似三角形的射線,并在三角形外側(cè)顯示對(duì)應(yīng)文字,且小藍(lán)點(diǎn)變小白點(diǎn)。

當(dāng)用戶在上方輸入內(nèi)容后,將內(nèi)容添加至下方的圓環(huán)周圍。如上圖所示。

筆者本來(lái)一開始的想法是使用css來(lái)實(shí)現(xiàn),就像下圖的動(dòng)態(tài)二級(jí)菜單一樣。

​ ​​

但是考慮到圓環(huán)邊緣的內(nèi)容可變,又要定位到圓環(huán)周圍,css可能會(huì)比較難實(shí)現(xiàn)。所以哇,筆者決定使用canvas來(lái)實(shí)現(xiàn)。(筆者最近才學(xué)的canvas,有什么不對(duì)的,接受大家的指正)。

實(shí)現(xiàn)過(guò)程:

首先:

html部分代碼如下:

<canvas style="margin-left: 50px;padding-top: 20px; display:block;" id="canvas" > 您的瀏覽器當(dāng)前版本不支持canvas</canvas>

具體實(shí)現(xiàn)步驟如下:

1、繪制大圓環(huán)。

使用canvas方法:context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);

x,y:圓心坐標(biāo),radius:圓心半徑,startAngle:繪制起始弧度,endAngle:繪制結(jié)束弧度, [, anticlockwise]:可選參數(shù),順時(shí)針還是逆時(shí)針繪制圓弧。

為了繪制方便,筆者將畫布的原點(diǎn)由之前的左上角,移動(dòng)至畫布的中心。

筆者計(jì)算的圓環(huán)的半徑為 r-80

canvas.width = 500canvas.height = 500//計(jì)算畫布中心位置的半徑let r = 500 / 2// 界面初始化的時(shí)候?qū)嫴嫉脑c(diǎn)移動(dòng)至畫布中心ctx.translate(r,r) //將畫筆移動(dòng)到圓形

具體代碼如下:

// 畫布初始化let canvas = document.getElementById('canvas')let  ctx= canvas.getContext('2d')let ratio = getPixelRato(ctx)canvas.width = 500canvas.height = 500//計(jì)算畫布中心位置的半徑let r = 500 / 2// 界面初始化的時(shí)候?qū)嫴嫉脑c(diǎn)移動(dòng)至畫布中心ctx.translate(r,r) //將畫筆移動(dòng)到圓形ctx.lineWidth = 3; //設(shè)置畫筆的線寬ctx.beginPath(); //畫筆開始// 繪制圓環(huán)邊緣漸變邊緣顏色var arcColor = ctx.createLinearGradient(-170, -170, 0, 170)arcColor.addColorStop(0, '#8ec1ff')arcColor.addColorStop(0.2, '#83beff')arcColor.addColorStop(0.5, '#75b1ff')arcColor.addColorStop(0.7,'#5998ff')arcColor.addColorStop(1, '#2065ff')ctx.strokeStyle= arcColor;//設(shè)置畫筆的顏色ctx.arc(0,0,r - 80,0,2*Math.PI,false)  //繪制圓形,坐標(biāo)0,0,半徑250-80,整圓(0-360度),false表示順時(shí)針ctx.closePath()ctx.stroke() //繪圖

​繪制結(jié)果如下

2、繪制圓環(huán)中部背景圖片(當(dāng)前畫布原點(diǎn)為畫布中心)

drawImage(image, dx, dy, dWidth, dHeight)

image:Canvas圖片資源,如<img>圖片,SVG圖像,Canvas元素本身等。

dx、dy:在Canvas畫布上規(guī)劃一片區(qū)域用來(lái)放置圖片,dx就是這片區(qū)域的左上角橫、縱坐標(biāo)。

dWidth、dHeight:在Canvas畫布上規(guī)劃一片區(qū)域用來(lái)放置圖片,這片區(qū)域的寬度、高度。

以下坐標(biāo)都是筆者計(jì)算得出

let image = new Image()image.src = 'image/quan.png'image.onload = () => {    // 原點(diǎn)移動(dòng)至中心    ctx.drawImage(image,-140,-140,280,280)}

繪制結(jié)果如下:

3、繪制圓環(huán)上的文字,小圓點(diǎn)(當(dāng)前畫布原點(diǎn)為畫布中心)

文字和小圓點(diǎn)的繪制目標(biāo):

3.1 小圓點(diǎn)均勻的顯示在大圓環(huán)上

3.2 文字散落在小圓點(diǎn)外方一點(diǎn)點(diǎn)

解決思路:

1、筆者使用一個(gè)數(shù)組來(lái)存儲(chǔ)當(dāng)前的詞語(yǔ)

let textArr = ['海闊天空','技術(shù)能力','資金雄厚','維修控制','安居樂(lè)業(yè)','走馬觀花','畫龍點(diǎn)睛','去其糟粕','逆風(fēng)而行','職業(yè)發(fā)展']

2、因?yàn)樾A點(diǎn)的個(gè)數(shù)以及詞語(yǔ)的個(gè)數(shù)是一樣的,它們兩個(gè)的個(gè)數(shù)也就是上方數(shù)組textArr的length

3、一個(gè)整圓的弧度是2π,要讓小圓點(diǎn)們均分圓環(huán),筆者首先計(jì)算出每個(gè)小圓點(diǎn)所在點(diǎn)的弧度

for(let i = 0;i<lengths;i++){ // 計(jì)算弧度 let rad = 2*Math.PI/lengths*i}

4、根據(jù)三角函數(shù)可以計(jì)算出當(dāng)前小圓點(diǎn)在畫布上的坐標(biāo)(x,y)(當(dāng)前畫布原點(diǎn)為畫布中心)

其中弧度,小圓點(diǎn),圓環(huán),圓環(huán)半徑,畫布原點(diǎn)關(guān)系,筆者畫了一個(gè)圖來(lái)描述它們。

計(jì)算文字的坐標(biāo):

// 計(jì)算小圓心坐標(biāo)let x = (r - 40)*Math.cos(rad)let y = (r - 40)*Math.sin(rad)

計(jì)算小圓點(diǎn)的坐標(biāo):因?yàn)樾A點(diǎn)的圓心都要落在圓環(huán)上,所以其計(jì)算橫縱坐標(biāo)是,

// 計(jì)算文字的坐標(biāo)    let x = (r - 80)*Math.cos(rad) let y = (r - 80)*Math.sin(rad)

具體代碼如下:

// 繪制文字ctx.font = '13px Arial'ctx.textAlign = 'center'ctx.textBaseline = 'middle'ctx.fillStyle="#000000"let lengths = textArr.lengthtextArr.forEach(function(text,i){    //弧度    let rad = 2*Math.PI/lengths*i    // 計(jì)算小圓心坐標(biāo)    let x = (r - 40)*Math.cos(rad)    let y = (r - 40)*Math.sin(rad)    ctx.fillText(text,x+0.5,y+0.5)});// 繪制小圓點(diǎn)for(let i = 0;i<lengths;i++){    // //    let rad = 2*Math.PI/lengths*i    let x = (r - 80)*Math.cos(rad)    let y = (r - 80)*Math.sin(rad)// // 繪制邊緣灰色半透明小圓點(diǎn)    ctx.beginPath()    ctx.fillStyle = 'rgba(226,235,250,0.8)'    ctx.arc(x,y,8,0,2*Math.PI,false)    ctx.closePath()    ctx.fill()    // 繪制藍(lán)色小圓點(diǎn)    ctx.beginPath()    ctx.fillStyle = '#208fe5'    ctx.arc(x,y,4,0,2*Math.PI,false)    ctx.closePath()    ctx.fill()        }

繪制結(jié)果如下:

4、繪制每個(gè)小圓點(diǎn)外面的三角形(當(dāng)前畫布原點(diǎn)為畫布中心)

4.1 因?yàn)橐L制出三角形的形狀,繪制三角形的思路就是,以當(dāng)前小圓點(diǎn)的圓心為起點(diǎn)向兩側(cè)畫條線,然后使用ctx.fill()封閉圖形,并使用漸變色填充內(nèi)部。

繪制三角形:坐標(biāo)自行計(jì)算。筆者是橫坐標(biāo)加減35.縱坐標(biāo)加上70(隨意隨意,看你喜歡,哈哈哈)

//畫筆開始        ctx.beginPath()     ctx.moveTo(x,y)    ctx.lineTo(x-35,y+70)    ctx.lineTo(x+35,y+70)    ctx.closePath()

繪制三角形下方的文字:(為了和之前的文字有區(qū)別,這里我文字我使用了紅色)

ctx.fillStyle= '#e3211c' ctx.fillText(textArr[i],x,y+75)

具體代碼如下:

for(let i = 0;i<lengths;i++){    // //    let rad = 2*Math.PI/lengths*i    let x = (r - 80)*Math.cos(rad)    let y = (r - 80)*Math.sin(rad)    // // 畫s三角形    // // ctx.rotate( -Math.PI / 4)    ctx.beginPath() //畫筆開始    ctx.moveTo(x,y)    ctx.lineTo(x-35,y+70)    ctx.lineTo(x+35,y+70)    ctx.closePath()    // // 設(shè)置 顏色 漸變--->從中心向兩邊添加顏色    var sColor = ctx.createLinearGradient (x,y,x+18,y+50)    sColor.addColorStop(0,'rgba(106,128,243,0.5)')    sColor.addColorStop(0.6,'rgba(83,183,243,0.5)')    sColor.addColorStop(0.7,'rgba(129,200,224,0.5)')    sColor.addColorStop(0.8,'rgba(130,219,251,0.5)')    sColor.addColorStop(1,'rgba(195,228,223,0.5)')    ctx.fillStyle= sColor    ctx.fill()    ctx.fillStyle= '#e3211c'    ctx.fillText(textArr[i],x,y+75)}

繪制結(jié)果如下:

4.2 需求是每個(gè)三角形的方向是向外散發(fā),而現(xiàn)在三角形的方向都是朝下方,所以現(xiàn)在需要使用canvas的旋轉(zhuǎn)方法。

ctx.save()    ctx.translate(x,y) // 旋轉(zhuǎn)角度以每個(gè)小圓點(diǎn)為中心    ctx.rotate( rad - Math.PI/2 ) // 因?yàn)橐婚_始小圓點(diǎn)    ctx.translate(-x, -y)    .    省略畫三角形和文字的代碼    .    .    ctx.restore()

由計(jì)算可得,以小圓點(diǎn)的圓心為旋轉(zhuǎn)起點(diǎn),三角形的旋轉(zhuǎn)的弧度應(yīng)該是當(dāng)前小圓點(diǎn)的弧度減去π/2,因?yàn)樾D(zhuǎn)的起始位置都是從x坐標(biāo)軸正方向開始,即弧度為0處開始,但是現(xiàn)在三角形的已經(jīng)都處于π/2弧度處,所以:

旋轉(zhuǎn)的弧度 = 小圓點(diǎn)的弧度 - π/2

記得旋轉(zhuǎn)的時(shí)候一定要使用Canvas狀態(tài)的存儲(chǔ)方法save()。

restore(),依次從堆棧的上方彈出存儲(chǔ)的Canvas狀態(tài),如果沒(méi)有任何存儲(chǔ)的Canvas狀態(tài),則執(zhí)行此方法沒(méi)有任何變化。

一定要記得最后要使用restore()方法,說(shuō)到這里,筆者留下了悔恨的淚水。。。

具體代碼:

for(let i = 0;i<lengths;i++){    // //    let rad = 2*Math.PI/lengths*i    let x = (r - 80)*Math.cos(rad)    let y = (r - 80)*Math.sin(rad)    // 畫s三角形    ctx.save()    // 旋轉(zhuǎn)角度以每個(gè)小圓點(diǎn)為中心  因?yàn)橐婚_始小圓點(diǎn)    ctx.translate(x,y)     ctx.rotate( rad - Math.PI/2 )     ctx.translate(-x, -y)    // 畫筆開始    ctx.beginPath()    ctx.moveTo(x,y)    ctx.lineTo(x-35,y+70)    ctx.lineTo(x+35,y+70)    ctx.closePath()    //設(shè)置 顏色 漸變--->從中心向兩邊添加顏色    var sColor = ctx.createLinearGradient (x,y,x+18,y+50)    sColor.addColorStop(0,'rgba(106,128,243,0.5)')    sColor.addColorStop(0.6,'rgba(83,183,243,0.5)')    sColor.addColorStop(0.7,'rgba(129,200,224,0.5)')    sColor.addColorStop(0.8,'rgba(130,219,251,0.5)')    sColor.addColorStop(1,'rgba(195,228,223,0.5)')    ctx.fillStyle= sColor    ctx.fill()    ctx.fillStyle= '#e3211c'    ctx.fillText(textArr[i],x,y+75)    ctx.restore()}

繪制結(jié)果:

定睛一看,what???有些文字因?yàn)樾D(zhuǎn)問(wèn)題,顛倒了,通過(guò)觀察得出結(jié)果,當(dāng)弧度大于π的時(shí)候,文字才出現(xiàn)顛倒問(wèn)題。

是時(shí)候?qū)懸徊╥f判斷了。。。。

旋轉(zhuǎn)文字的方法:

function rotateContext(ctx, x, y, degree) {            // 旋轉(zhuǎn)文字            ctx.translate(x, y)            // ctx.rotate(degree * Math.PI / 180)            ctx.rotate(degree)            ctx.translate(-x, -y)        }

判斷弧度大于π的小圓點(diǎn)

if (rad > Math.PI) {    // 因?yàn)槲淖中枰@示在三角形的邊緣,所以文字應(yīng)該隨著三角形旋轉(zhuǎn),才能一直維持在    // 三角形的邊緣,由于旋轉(zhuǎn)后當(dāng)弧度大于π的值都會(huì)出現(xiàn)文字倒轉(zhuǎn)問(wèn)題,于是將文字進(jìn)行旋轉(zhuǎn)翻轉(zhuǎn)    ctx.save()    ctx.beginPath()    // 旋轉(zhuǎn)文字    rotateContext(ctx, x, y+75, Math.PI)    ctx.font = '13px Arial'    ctx.textAlign = 'center'    ctx.fillStyle = "#ff2238"    ctx.fillText(textArr[i], x, y+ 75)    ctx.restore()} else {    ctx.fillStyle = '#ff2238'    ctx.fillText(textArr[i], x, y + 75)}

繪制結(jié)果如下:

勝利再望,快要成功了,至少大概布局有了,革命尚未成功,同志仍需努力!!

5、下面就是實(shí)現(xiàn),鼠標(biāo)在小圓點(diǎn)上方,讓邊緣的三角形和三角形邊緣文字顯示,而圓環(huán)邊的文字不顯示

思路:

1、給畫布綁定鼠標(biāo)進(jìn)入事件

2、判斷當(dāng)前鼠標(biāo)所在畫布位置的坐標(biāo)是否等于某個(gè)小圓點(diǎn)的附近的坐標(biāo),如果等于就顯示對(duì)應(yīng)小圓點(diǎn)的三角形。

5.1給canvas畫布綁定mousemove事件:鼠標(biāo)在上方事件

canvas.addEventListener('mousemove',clickEvent)

5.2 計(jì)算鼠標(biāo)當(dāng)前在畫布上的坐標(biāo)

計(jì)算方法是:使用鼠標(biāo)當(dāng)前在dom上的坐標(biāo)減去,畫布距離左方或上方的距離,計(jì)算出畫布的距離

下圖的drawOne方法為繪制方法,文章后續(xù)會(huì)說(shuō)到。

function clickEvent() {            // 鼠標(biāo)所在位置坐標(biāo)            let x = event.clientX - canvas.getBoundingClientRect().left            let y = event.clientY - canvas.getBoundingClientRect().top            drawOne(x,y)}

5.3,因?yàn)樯戏接?jì)算出來(lái)的鼠標(biāo)在畫布上的坐標(biāo)是以畫布的左上角為原點(diǎn)計(jì)算的坐標(biāo),但是當(dāng)前畫布的原點(diǎn)早已移動(dòng)到畫布中心(250,250)處,所以當(dāng)用來(lái)判斷是否是點(diǎn)擊某個(gè)小圓點(diǎn)的時(shí)候需要橫縱坐標(biāo)都減去250,才能與當(dāng)前畫布的小圓點(diǎn)坐標(biāo)進(jìn)行比哦對(duì),筆者在判斷的時(shí)候,發(fā)現(xiàn) 一個(gè)問(wèn)題,不知道為啥筆者的y方向的差量是260而不是250,所以筆者y方向上都減去了260。

代碼如下:

其中Cx,Cy為鼠標(biāo)在畫布上的坐標(biāo)(以畫布左上角為原點(diǎn)),x,y為當(dāng)前小圓點(diǎn)的坐標(biāo),

筆者直接計(jì)算出小圓點(diǎn)圓心附近15px的位置,都顯示三角形,和小圓點(diǎn)變白色。

最主要的是每次重新繪制都需要清空之前的畫布:記住使用clearRect方法清空畫布

let XX = Cx - 250let YY = Cy- 260let leftX = x - 15 let rightX = x + 15let topY = y - 15let bottomY = y + 15if (XX >= leftX && XX <= rightX && YY <= bottomY && YY >= topY ) {//就是它被點(diǎn)了。。。。。。//這中間寫繪制的代碼}

代碼后續(xù)附上鏈接:

6,界面上定義一個(gè)Input,給input綁定change事件。

實(shí)現(xiàn):每一次Input內(nèi)的值改變都重繪界面。

html代碼:

<input type="text" id="inpt"  style="margin-left: 100px;margin-top: 50px" placeholder="請(qǐng)輸入...">

js代碼:

let inpt = document.getElementById('inpt') inpt.addEventListener('change', function () {     if (inpt.value !== '') {        textArr.push(inpt.value)        drawAll(2)  //此方法是繪制的方法,文章后續(xù)給源代碼    }})

7,出現(xiàn)了一個(gè)問(wèn)題,當(dāng)每次點(diǎn)擊界面,重繪界面的時(shí)候都會(huì)出現(xiàn)一閃一閃的狀況

如下所示:

每次滑動(dòng),因?yàn)槭髽?biāo)的坐標(biāo)改變了,都需要清空?qǐng)A環(huán)周圍的的內(nèi)容,重新繪制。所以就需要清空畫布達(dá)到動(dòng)效的效果。

clearRect()在Canvas動(dòng)畫繪制中非常常用,不斷清除畫布內(nèi)容再繪制,形成動(dòng)畫效果。

clearRect()可以把Canvas元素畫布中的某一塊矩形區(qū)域變成透明的。

context.clearRect(x, y, width, height);

x、y:矩形左上角x、y坐標(biāo)。

width、heigh:被清除的矩形區(qū)域的寬度、高度。

由于clearRect()只能清除矩形區(qū)域的畫布,所以每次清除的時(shí)候,中間的背景圖片都會(huì)一塊兒被清除。

所以每次都要重新加載背景圖片,加載圖片又是有一定的時(shí)間的,所以出現(xiàn)沒(méi)次都會(huì)閃一下。

解決方案:

drawImage(image, dx, dy, dWidth, dHeight)

其中參數(shù)image:Canvas圖片資源,如<img>圖片,SVG圖像,Canvas元素本身等。

那可以使用其他canvas來(lái)緩存圖片方式。

使用額外的canvas來(lái)繪制出背景圖片,但是對(duì)于那個(gè)canvas不顯示在界面:display:none,然后使用當(dāng)清空畫布后,直接將緩存起來(lái)的canvals畫布對(duì)象,渲染到要顯示的畫布中間,就是不用再去加載一次圖片,加載圖片是比較耗時(shí)的。

html代碼:

<canvas width="280" height="280" style="margin-left: 50px;padding-top: 20px; display:none;" id="canvas2">    </canvas>

js代碼:

// 利用緩存來(lái)解決重繪圖片閃動(dòng)問(wèn)題var tempCanvas = document.getElementById('canvas2')const tempCtx = tempCanvas.getContext('2d')tempCanvas.width = 280; tempCanvas.height = 280let image = new Image()image.src = 'image/quan.png'image.onload = () => {    // 原點(diǎn)移動(dòng)至中心    tempCtx.drawImage(image,0,0,280,280)}

當(dāng)清除畫布后,重新繪制圖片的時(shí)候直接將緩存canvas:tempCanvas繪制出來(lái)

// 將緩存的canvas直接繪制到界面(緩存了中間輪胎界面) ctx.drawImage(tempCanvas,-140,-140)

好啦,成功了,獻(xiàn)上成果圖:

源代碼地址如下:

https://github.com/Linefate/Dynamic-effect-of-canvas-ring.git

總結(jié)

以上所述是小編給大家介紹的使用html5 canvas繪制圓環(huán)動(dòng)效,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)武林網(wǎng)網(wǎng)站的支持!

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 久久99精品久久久久久园产越南 | 亚洲xxx视频 | 欧美一级淫片免费播放口 | 午夜在线视频一区二区三区 | 一区二区免费网站 | 国产一级伦理片 | 色人阁五月天 | 欧美视频一区二区三区四区 | 一区二区三区日韩在线 | 欧美成人一区二区三区电影 | 日本一级黄色大片 | 欧美成人精品欧美一级乱黄 | 国产一极毛片 | 免费观看国产精品视频 | av在线试看| 日本高清视频网站www | a免费毛片 | 免费看一级片 | 一区二区三高清 | 免费看成人毛片 | 亚洲欧美不卡视频 | 美女久久 | 国产精品福利一区 | 黄视频网站免费观看 | 精品亚洲免费 | 精品亚洲一 | 中文字幕在线网 | 国产成人午夜精品 | 国产精品成人久久久久a级 av电影在线免费 | 国产一级一区 | 在线a毛片免费视频观看 | 午夜小电影| 毛片久久| 色女生影院 | 国产手机国产手机在线 | 舌头伸进添的我好爽高潮网站 | 日本在线免费观看视频 | 毛片在线播放视频 | 欧美国产第一页 | 中国3xxxx | www.99热精品|