应用html5 canvas绘图圆环动效

2021-02-22 23:28 jianzhan

近期笔者有个要求,要求內容为:1组文本显示信息在圆环的周边,客户可加上文本,文本紧紧围绕着圆环,每一个词对应圆环周边的的蓝色小圆点,当客户电脑鼠标放在圆环上方小蓝点时刻,完成放射性出3角形,再显示信息出文本,先看看动图实际效果吧!

​ ​​

如上图所示,当电脑鼠标放在对应蓝色小圆点上时,必须放射性出射相近3角形的射线,并在3角形外侧显示信息对应文本,且小蓝点缩小白点。

当客户在上方键入內容后,将內容加上至正下方的圆环周边。如上图所示。

笔者原本1刚开始的念头是应用css来完成,就像下图的动态性2级菜单1样。

​ ​​

可是考虑到到圆环边沿的內容可变,又要精准定位到圆环周边,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⑻0

canvas.width = 500
canvas.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 = 500
canvas.height = 500
//测算画布管理中心部位的半径
let r = 500 / 2
// 页面原始化的情况下将画布的原点挪动至画布管理中心
ctx.translate(r,r) //将画笔挪动到圆形
ctx.lineWidth = 3; //设定画笔的线宽
ctx.beginPath(); //画笔刚开始
// 绘图圆环边沿渐变色边沿色调
var arcColor = ctx.createLinearGradient(⑴70, ⑴70, 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;//设定画笔的色调
ctx.arc(0,0,r - 80,0,2*Math.PI,false)  //绘图圆形,座标0,0,半径250⑻0,整圆(0⑶60度),false表明顺时针
ctx.closePath()
ctx.stroke() //制图

​绘图結果以下

2、绘图圆环中部情况照片(当今画布原点为画布管理中心)

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

image:Canvas照片資源,如<img>照片,SVG图象,Canvas元素自身等。

dx、dy:在Canvas画布上整体规划1片地区用来置放照片,dx便是这片地区的左上角横、纵座标。

dWidth、dHeight:在Canvas画布上整体规划1片地区用来置放照片,这片地区的宽度、高宽比。

下列座标全是笔者测算得出

let image = new Image()
image.src = 'image/quan.png'
image.onload = () => {
    // 原点挪动至管理中心
    ctx.drawImage(image,⑴40,⑴40,280,280)
}

绘图結果以下:

3、绘图圆环上的文本,小圆点(当今画布原点为画布管理中心)

文本和小圆点的绘图总体目标:

3.1 小圆点匀称的显示信息在大圆环上

3.2 文本散落在小圆点外方1点点

处理思路:

1、笔者应用1个数字能量数组来储存当今的词语

let textArr = ['开阔天空','技术性工作能力','资金雄厚','检修操纵','国泰民安','走马看花','画龙点晴','取其精华','逆风翻盘而行','岗位发展趋势']

2、由于小圆点的个数和词语的个数是1样的,它们两个的个数也便是上方数字能量数组textArr的length

3、1个整圆的弧度是2π,要让小圆点们平均分圆环,笔者最先测算出每一个小圆点所属点的弧度

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

4、依据3角涵数能够测算出当今小圆点在画布上的座标(x,y)(当今画布原点为画布管理中心)

在其中弧度,小圆点,圆环,圆环半径,画布原点关联,笔者画了1个图来叙述它们。

测算文本的座标:

// 测算小圆心座标
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="#000000"
let lengths = textArr.length
textArr.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、绘图每一个小圆点外面的3角形(当今画布原点为画布管理中心)

4.1 由于要绘图出3角形的样子,绘图3角形的思路便是,以当今小圆点的圆心为起始点向两边画条线,随后应用ctx.fill()封闭式图型,并应用渐变色色填充內部。

绘图3角形:座标自主测算。笔者是横座标加减35.纵座标再加70(随便随便,看你喜爱,哈哈哈)

//画笔刚开始    
    ctx.beginPath() 
    ctx.moveTo(x,y)
    ctx.lineTo(x⑶5,y+70)
    ctx.lineTo(x+35,y+70)
    ctx.closePath()

绘图3角形正下方的文本:(以便和以前的文本有差别,这里我文本我应用了鲜红色)

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)
    // // 画s3角形
    // // ctx.rotate( -Math.PI / 4)
    ctx.beginPath() //画笔刚开始
    ctx.moveTo(x,y)
    ctx.lineTo(x⑶5,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.addColorStop(1,'rgba(195,228,223,0.5)')

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

绘图結果以下:

4.2 要求是每一个3角形的方位是向外释放,而如今3角形的方位全是朝正下方,因此如今必须应用canvas的转动方式。

ctx.save()
    ctx.translate(x,y) // 转动角度以每一个小圆点为管理中心
    ctx.rotate( rad - Math.PI/2 ) // 由于1刚开始小圆点
    ctx.translate(-x, -y)
    .
    省略画3角形和文本的编码
    .
    .
    ctx.restore()

由测算可得,以小圆点的圆心为转动起始点,3角形的转动的弧度应当是当今小圆点的弧度减去π/2,由于转动的起止部位全是从x座标轴正方位刚开始,即弧度为0处刚开始,可是如今3角形的早已都处在π/2弧度处,因此:

转动的弧度 = 小圆点的弧度 - π/2

记得转动的情况下1定要应用Canvas情况的储存方式save()。

restore(),先后从堆栈的上方弹出储存的Canvas情况,假如沒有任何储存的Canvas情况,则实行此方式沒有任何转变。

1定要记得最终要应用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)
    // 画s3角形
    ctx.save()
    // 转动角度以每一个小圆点为管理中心  由于1刚开始小圆点
    ctx.translate(x,y) 
    ctx.rotate( rad - Math.PI/2 ) 
    ctx.translate(-x, -y)
    // 画笔刚开始
    ctx.beginPath()
    ctx.moveTo(x,y)
    ctx.lineTo(x⑶5,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.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()
}

绘图結果:

定睛1看,what???一些文本由于转动难题,错乱了,根据观查得出結果,当弧度超过π的情况下,文本才出現错乱难题。

是情况下写1波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) {
    // 由于文本必须显示信息在3角形的边沿,因此文本应当伴随着3角形转动,才可以1直保持在
    // 3角形的边沿,因为转动后当弧度超过π的值都会出現文本倒转难题,因而将文本开展转动旋转
    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'
    ctx.fillText(textArr[i], x, y + 75)
}

绘图結果以下:

获胜再望,快要取得成功了,最少大约合理布局有了,改革并未取得成功,朋友仍需勤奋!!

5、下面便是完成,电脑鼠标在小圆点上方,让边沿的3角形和3角形边沿文本显示信息,而圆环边的文本无法显示

思路:

1、给画布关联电脑鼠标进到恶性事件

2、分辨当今电脑鼠标所属画布部位的座标是不是等于某个小圆点的周边的座标,假如等于就显示信息对应小圆点的3角形。

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,才可以与当今画布的小圆点座标开展比哦对,笔者在分辨的情况下,发现 1个难题,不知道道为啥笔者的y方位的差量是260而并不是250,因此笔者y方位上都减去了260。

编码以下:

在其中Cx,Cy为电脑鼠标在画布上的座标(以画布左上角为原点),x,y为当今小圆点的座标,

笔者立即测算出小圆点圆心周边15px的部位,都显示信息3角形,和小圆点变白色。

最关键的是每次再次绘图都必须清空以前的画布:记牢应用clearRect方式清空画布

let XX = Cx - 250
let YY = Cy- 260
let leftX = x - 15
 let rightX = x + 15
let topY = y - 15
let bottomY = y + 15
if (XX >= leftX && XX <= rightX && YY <= bottomY && YY >= topY ) {
//便是它被点了。。。。。。
//这正中间写绘图的编码
}

编码后续附上连接:

6,页面上界定1个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,出現了1个难题,当每次点一下页面,重绘页面的情况下都会出現1闪1闪的情况

以下所示:

每次拖动,由于电脑鼠标的座标更改了,都必须清空圆环周边的的內容,再次绘图。因此就必须清空画布做到动效的实际效果。

clearRect()在Canvas动漫绘图中十分常见,持续消除画布內容再绘图,产生动漫实际效果。

clearRect()能够把Canvas元素画布中的某1块矩形框地区变为全透明的。

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

x、y:矩形框左上角x、y座标。

width、heigh:被消除的矩形框地区的宽度、高宽比。

因为clearRect()只能消除矩形框地区的画布,因此每次消除的情况下,正中间的情况照片都会1块儿被消除。

因此每次都要再次载入情况照片,载入照片又是有1定的時间的,因此出現没次都会闪1下。

处理计划方案:

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

在其中主要参数image:Canvas照片資源,如<img>照片,SVG图象,Canvas元素自身等。

那可使用别的canvas来缓存文件照片方法。

应用附加的canvas来绘图出情况照片,可是针对那个canvas无法显示在页面:display:none,随后应用当清空画布后,立即将缓存文件起来的canvals画布目标,3D渲染到要显示信息的画布正中间,便是无需再去载入1次照片,载入照片是较为耗时的。

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 = 280
let image = new Image()
image.src = 'image/quan.png'
image.onload = () => {
    // 原点挪动至管理中心
    tempCtx.drawImage(image,0,0,280,280)
}

当消除画布后,再次绘图照片的情况下立即将缓存文件canvas:tempCanvas绘图出来

// 将缓存文件的canvas立即绘图到页面(缓存文件了正中间轮胎页面)
 ctx.drawImage(tempCanvas,⑴40,⑴40)

好啦,取得成功了,献上成效图:

源码详细地址以下:

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

总结

以上所述是网编给大伙儿详细介绍的应用html5 canvas绘图圆环动效,期待对大伙儿有一定的协助,假如大伙儿有任何疑惑请给我留言,网编会立即回应大伙儿的。在此也十分谢谢大伙儿对脚本制作之家网站的适用!