最近筆者有個需求,需求內容為:一組文字顯示在圓環的周圍,用戶可添加文字,文字圍繞著圓環,每個詞對應圓環周圍的藍色小圓點,當用戶鼠標放在圓環上方小藍點時,實現放射出三角形,再顯示出文字,先看看動圖效果吧!
如上圖所示,當滑鼠放在對應藍色小圓點上時,需要放射出射類似三角形的射線,並在三角形外側顯示對應文字,且小藍點變小白點。
號
當使用者在上方輸入內容後,將內容加入下方的圓環周圍。如上圖所示。
筆者本來一開始的想法是使用css來實現,就像下圖的動態二級選單一樣。
但考慮到圓環邊緣的內容可變,又要定位到圓環周圍,css可能會比較難實現。所以哇,筆者決定使用canvas來實現。 (筆者最近才學的canvas,有什麼不對的,接受大家的指正)。
實現過程:首先:
html部分程式碼如下:
<canvas style=margin-left: 50px;padding-top: 20px; display:block; id=canvas > 您的瀏覽器目前版本不支援canvas</canvas>
具體實作步驟如下:
1、繪製大圓環。
使用canvas方法:context.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
x,y:圓心座標,radius:圓心半徑,startAngle:繪製起始弧度,endAngle:繪製結束弧度, [, anticlockwise]:可選參數,順時針還是逆時針繪製圓弧。
為了繪製方便,筆者將畫布的原點從先前的左上角,並移動至畫布的中心。
筆者計算的圓環的半徑為r-80
canvas.width = 500canvas.height = 500//計算畫布中心位置的半徑let r = 500 / 2// 介面初始化的時候將畫布的原點移到畫布中心ctx.translate(r,r) //將畫筆移到到圓形
具體代碼如下:
// 畫布初始化let canvas = document.getElementById('canvas')let ctx= canvas.getContext('2d')let ratio = getPixelRato(ctx)canvas.width = 500canvas.height = 500//計算畫布中心位置的半徑let r = 500 / 2//介面初始化的時候將畫布的原點移到畫布中心ctx.translate(r,r) //將畫筆移到圓形ctx.lineWidth = 3; //設定畫筆的線寬ctx.beginPath(); //畫筆開始// 繪製圓環邊緣漸變邊緣顏色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,'#598. 1, '#2065ff')ctx.strokeStyle= arcColor;//設定畫筆的顏色ctx.arc(0,0,r - 80,0,2*Math.PI,false) //繪製圓形,座標0,0,半徑250-80,整圓(0-360度),false表示順時針ctx.closePath()ctx.stroke() //繪圖繪製結果如下
號
2.繪製圓環中部背景圖片(目前畫布原點為畫布中心)
drawImage(image, dx, dy, dWidth, dHeight)
image:Canvas圖片資源,如<img>圖片,SVG圖像,Canvas元素本身等。
dx、dy:在Canvas畫布上規劃一片區域用來放置圖片,dx就是這片區域的左上角橫、縱座標。
dWidth、dHeight:在Canvas畫布上規劃一片區域用來放置圖片,這區域的寬度、高度。
以下座標都是筆者計算得出
let image = new Image()image.src = 'image/quan.png'image.onload = () => { // 原點移至中心ctx.drawImage(image,-140,-140,280,280)}繪製結果如下:
號
3.繪製圓環上的文字,小圓點(目前畫布原點為畫布中心)
文字和小圓點的繪製目標:
3.1 小圓點均勻的顯示在大圓環上
3.2 文字散落在小圓點外一點
解決思路:1.筆者使用一個陣列來儲存目前的詞語
let textArr = ['海闊天空','技術能力','資金雄厚','維修控制','安居樂業','走馬觀花','畫龍點睛','去其糟粕','逆風而行','職業發展']
2.因為小圓點的個數以及詞語的個數是一樣的,它們兩個的個數也就是上方數組textArr的length
3.一個整圓的弧度是2π,要讓小圓點們均分圓環,筆者先計算出每個小圓點所在點的弧度
for(let i = 0;i<lengths;i++){ // 計算弧度let rad = 2*Math.PI/lengths*i}4.根據三角函數可以計算出目前小圓點在畫布上的座標(x,y)(目前畫布原點為畫布中心)
號
其中弧度,小圓點,圓環,圓環半徑,畫布原點關係,筆者畫了一個圖來描述它們。
號
計算文字的座標:
// 計算小圓心座標let x = (r - 40)*Math.cos(rad)let y = (r - 40)*Math.sin(rad)
計算小圓點的座標:因為小圓點的圓心都要落在圓環上,所以其計算橫縱座標是,
// 計算文字的座標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=#000000let lengths = textArr.lengthtextArr.forEach(function(text,i){ //弧度let rad = 2*Math.PI/lengths*i // 計算小圓心座標let x = (r - 40)*Math.cos(rad) let y = (r - 40)*Math.sin(rad) ctx.fillText(text,x+0.5,y+0.5)});// 畫出小圓點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)// // 繪製邊緣灰色半透明小圓點ctx.beginPath() ctx.fillStyle = 'rgba(226,235,250, 0.8)' ctx.arc(x,y,8,0,2*Math.PI,false) ctx.closePath() ctx.fill() // 繪製藍色小圓點ctx.beginPath() ctx.fillStyle = '#208fe5' ctx.arc(x,y,4,0,2*Math.PI,false) ctx.closePath( ) ctx.fill() }繪製結果如下:
號
4.繪製每個小圓點外面的三角形(目前畫布原點為畫布中心)
4.1 因為要繪製出三角形的形狀,繪製三角形的想法就是,以當前小圓點的圓心為起點向兩側畫條線,然後使用ctx.fill()封閉圖形,並使用漸變色填充內部。
繪製三角形:座標自行計算。筆者是橫座標加減35.縱座標加上70(隨意隨意,看你喜歡,哈哈哈)
//畫筆開始ctx.beginPath() ctx.moveTo(x,y) ctx.lineTo(x-35,y+70) ctx.lineTo(x+35,y+70) ctx.closePath()
繪製三角形下方的文字:(為了和之前的文字有區別,這裡我文字我使用了紅色)
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() // // 設定顏色漸層-- ->從中心往兩邊加上顏色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,'r.是。 ctx.fillText(textArr[i],x,y+75)}繪製結果如下:
號
4.2 需求是每個三角形的方向是向外散發,而現在三角形的方向都是朝下方,所以現在需要使用canvas的旋轉方法。
ctx.save() ctx.translate(x,y) // 旋轉角度以每個小圓點為中心ctx.rotate( rad - Math.PI/2 ) // 因為一開始小圓點ctx.translate(- x, -y) . 省略畫三角形和文字的程式碼. . ctx.restore()
由計算可得,以小圓點的圓心為旋轉起點,三角形的旋轉的弧度應該是當前小圓點的弧度減去π/2,因為旋轉的起始位置都是從x座標軸正方向開始,即弧度為0處開始,但現在三角形的已經都處於π/2弧度處,所以:
旋轉的弧度= 小圓點的弧度- π/2
記得旋轉的時候一定要使用Canvas狀態的儲存方法save()。
restore(),依序從堆疊的上方彈出儲存的Canvas狀態,如果沒有任何儲存的Canvas狀態,則執行此方法沒有任何變更。
一定要記得最後要使用restore()方法,說到這裡,筆者留下了悔恨的淚水。 。 。
具體代碼:
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() // 旋轉角度以每個小圓點為中心因為一開始小圓點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() //設定顏色漸層--->從中心往兩邊加入顏色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.addorStop(1,'rba2( ctx.fillStyle= sColor ctx.fill() ctx.fillStyle= '#e3211c' ctx.fillText(textArr[i],x,y+75) ctx.restore()}繪製結果:
號
定睛一看,what? ? ?有些文字因為旋轉問題,顛倒了,透過觀察得出結果,當弧度大於π的時候,文字才出現顛倒問題。
是時候寫一波if判斷了。 。 。 。
旋轉文字的方法:
function rotateContext(ctx, x, y, degree) { // 旋轉文字ctx.translate(x, y) // ctx.rotate(degree * Math.PI / 180) ctx.rotate(degree) ctx.translate(-x , -y) }判斷弧度大於π的小圓點
if (rad > Math.PI) { // 因為文字需要顯示在三角形的邊緣,所以文字應該隨著三角形旋轉,才能一直維持在// 三角形的邊緣,由於旋轉後當弧度大於π的值都會出現文字倒轉問題,於是將文字進行旋轉翻轉ctx.save() ctx.beginPath() // 旋轉文字 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' x.fillSpill [i], x, y + 75)}繪製結果如下:
號
勝利再望,快要成功了,至少大概佈局有了,革命尚未成功,同志仍需努力! !
5.下面就是實現,滑鼠在小圓點上方,讓邊緣的三角形和三角形邊緣文字顯示,而圓環邊的文字不顯示
思路:1.給畫布綁定滑鼠進入事件
2.判斷目前滑鼠所在畫布位置的座標是否等於某個小圓點的附近的座標,如果等於就顯示對應小圓點的三角形。
5.1給canvas畫布綁定mousemove事件:滑鼠在上方事件
canvas.addEventListener('mousemove',clickEvent)5.2 計算滑鼠目前在畫布上的座標
計算方法為:使用滑鼠目前在dom上的座標減去,畫布距離左方或上方的距離,計算畫布的距離
下圖的drawOne方法為繪製方法,文章後續會說到。
function clickEvent() { // 滑鼠所在位置座標let x = event.clientX - canvas.getBoundingClientRect().left let y = event.clientY - canvas.getBoundingClientRect().top drawOne(x,y)}5.3,因為上方計算出來的滑鼠在畫布上的座標是以畫布的左上角為原點計算的座標,但是當前畫布的原點早已移動到畫布中心(250,250)處,所以當用來判斷是否是點擊某個小圓點的時候需要橫縱座標都減去250,才能與目前畫布的小圓點座標進行比哦對,筆者在判斷的時候,發現一個問題,不知道為啥筆者的y方向的差量是260而不是250,所以筆者y方向上都減去了260。
程式碼如下:
其中Cx,Cy為滑鼠在畫布上的座標(以畫布左上角為原點),x,y為目前小圓點的座標,
筆者直接計算出小圓點圓心附近15px的位置,都顯示三角形,和小圓點變白色。
最主要的是每次重新繪製都需要清空之前的畫布:記得使用clearRect方法清空畫布
let XX = Cx - 250let YY = Cy- 260let leftX = x - 15 let rightX = x + 15let topY = y - 15let bottomY = y + 15if (XX >= leftX && XX <= righttomY = y + 15if (XX >= leftX && XX <= rightX &botYY = topY ) {//就是它被點了。 。 。 。 。 。 //這中間寫繪製的程式碼}代碼後續附上連結:
6,介面上定義一個Input,給input綁定change事件。
實作:每一次Input內的值改變都重繪介面。
html代碼:
<input type=text id=inpt style=margin-left: 100px;margin-top: 50px placeholder=請輸入...>
js程式碼:
let inpt = document.getElementById('inpt') inpt.addEventListener('change', function () { if (inpt.value !== '') { textArr.push(inpt.value) drawAll(2) //此方法是繪製的方法,文章後續給原始碼}})7,出現了一個問題,當每次點擊介面,重繪介面的時候都會出現一閃一閃的狀況
如下圖所示:
號
每次滑動,因為滑鼠的座標改變了,都需要清空圓環周圍的內容,重新繪製。所以就需要清空畫布達到動效的效果。
clearRect()在Canvas動畫繪製中非常常用,不斷清除畫布內容再繪製,形成動畫效果。
clearRect()可以把Canvas元素畫布中的某一塊矩形區域變成透明的。
context.clearRect(x, y, width, height);
x、y:矩形左上角x、y座標。
width、heigh:被清除的矩形區域的寬度、高度。
由於clearRect()只能清除矩形區域的畫布,所以每次清除的時候,中間的背景圖片都會一塊兒被清除。
所以每次都要重新載入背景圖片,載入圖片又是有一定的時間的,所以出現沒次都會閃一下。
解決方案:drawImage(image, dx, dy, dWidth, dHeight)
其中參數image:Canvas圖片資源,如<img>圖片,SVG影像,Canvas元素本身等。
那可以使用其他canvas來快取圖片方式。
使用額外的canvas來繪製出背景圖片,但是對於那個canvas不顯示在界面:display:none,然後使用當清空畫布後,直接將緩存起來的canvals畫布對象,渲染到要顯示的畫布中間,就是不用再去載入一次圖片,載入圖片是比較耗時的。
html代碼:
<canvas width=280 height=280 style=margin-left: 50px;padding-top: 20px; display:none; id=canvas2> </canvas>
js程式碼:
// 利用快取來解決重繪圖片閃爍問題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 = () => { // 原點移動至中心tempCtx.drawImage(image,0,0,280,280)}當清除畫布後,重新繪製圖片的時候直接將快取canvas:tempCanvas繪製出來
// 將快取的canvas直接繪製到介面(快取了中間輪胎介面) ctx.drawImage(tempCanvas,-140,-140)
好啦,成功了,獻上成果圖:
號
原始碼位址如下:
https://github.com/Linefate/Dynamic-effect-of-canvas-ring.git
總結以上所述是小編給大家介紹的使用html5 canvas繪製圓環動效,希望對大家有幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對VeVb武林網站的支持!