使用html5 canvas绘制圆环动效


Posted in HTML / CSS onJune 03, 2019

最近笔者有个需求,需求内容为:一组文字显示在圆环的周围,用户可添加文字,文字围绕着圆环,每个词对应圆环周围的的蓝色小圆点,当用户鼠标放在圆环上方小蓝点时时,实现放射出三角形,再显示出文字,先看看动图效果吧!

使用html5 canvas绘制圆环动效 ​ ​​

如上图所示,当鼠标放在对应蓝色小圆点上时,需要放射出射类似三角形的射线,并在三角形外侧显示对应文字,且小蓝点变小白点。

使用html5 canvas绘制圆环动效

当用户在上方输入内容后,将内容添加至下方的圆环周围。如上图所示。

笔者本来一开始的想法是使用css来实现,就像下图的动态二级菜单一样。

使用html5 canvas绘制圆环动效 ​ ​​

但是考虑到圆环边缘的内容可变,又要定位到圆环周围,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 = 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(-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;//设置画笔的颜色
ctx.arc(0,0,r - 80,0,2*Math.PI,false)  //绘制圆形,坐标0,0,半径250-80,整圆(0-360度),false表示顺时针
ctx.closePath()
ctx.stroke() //绘图

​绘制结果如下

使用html5 canvas绘制圆环动效

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)
}

绘制结果如下:

使用html5 canvas绘制圆环动效

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)(当前画布原点为画布中心)

使用html5 canvas绘制圆环动效

其中弧度,小圆点,圆环,圆环半径,画布原点关系,笔者画了一个图来描述它们。

使用html5 canvas绘制圆环动效

计算文字的坐标:

// 计算小圆心坐标
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()
    
    }

绘制结果如下:

使用html5 canvas绘制圆环动效

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,'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)
}

绘制结果如下:

使用html5 canvas绘制圆环动效

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.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()
}

绘制结果:

使用html5 canvas绘制圆环动效

定睛一看,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'
    ctx.fillText(textArr[i], x, y + 75)
}

绘制结果如下:

使用html5 canvas绘制圆环动效

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

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 - 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,界面上定义一个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,出现了一个问题,当每次点击界面,重绘界面的时候都会出现一闪一闪的状况

如下所示:

使用html5 canvas绘制圆环动效

每次滑动,因为鼠标的坐标改变了,都需要清空圆环周围的的内容,重新绘制。所以就需要清空画布达到动效的效果。

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 = 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,-140,-140)

好啦,成功了,献上成果图:

使用html5 canvas绘制圆环动效

源代码地址如下:

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

总结

以上所述是小编给大家介绍的使用html5 canvas绘制圆环动效,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

HTML / CSS 相关文章推荐
CSS3模块的目前的状况分析
Feb 24 HTML / CSS
CSS3盒子模型详解
Apr 24 HTML / CSS
CSS3实现时间轴效果
Jul 11 HTML / CSS
canvas实现有递增动画的环形进度条的实现方法
Jul 10 HTML / CSS
HTML5的结构和语义(1):前言
Oct 17 HTML / CSS
HTML5通过调用canvas对象的getContext()方法来获取绘图环境
Jun 23 HTML / CSS
HTML5实现可缩放时钟代码
Aug 28 HTML / CSS
html5使用canvas实现弹幕功能示例
Sep 11 HTML / CSS
浅谈基于Canvas的手绘风格图形库Rough.js
Mar 19 HTML / CSS
使用HTML5 Geolocation实现一个距离追踪器
Apr 09 HTML / CSS
Canvas环形饼图与手势控制的实现代码
Nov 08 HTML / CSS
老生常谈 使用 CSS 实现三角形的技巧(多种方法)
Apr 13 HTML / CSS
高清屏中使用Canvas绘图出现模糊的问题及解决方法
Jun 03 #HTML / CSS
H5最强接口之canvas实现动态图形功能
May 31 #HTML / CSS
如何在Canvas中添加事件的方法示例
May 21 #HTML / CSS
HTML5新增form控件和表单属性实例代码详解
May 15 #HTML / CSS
Html5 滚动穿透的方法
May 13 #HTML / CSS
JavaScript+Canvas实现自定义画板的示例代码
May 13 #HTML / CSS
使用html5实现表格实现标题合并的实例代码
May 13 #HTML / CSS
You might like
php中session使用示例
2014/03/29 PHP
linux下编译安装memcached服务
2014/08/03 PHP
php防止站外远程提交表单的方法
2014/10/20 PHP
深入浅析php json 格式控制
2015/12/24 PHP
实例讲解php数据访问
2016/05/09 PHP
PHP弱类型语言中类型判断操作实例详解
2017/08/10 PHP
PHP连接MySQL数据库操作代码实例解析
2020/07/11 PHP
学习ExtJS Column布局
2009/10/08 Javascript
两种简单实现菜单高亮显示的JS类代码
2010/06/27 Javascript
js转化毫秒为时间格式代码
2014/04/10 Javascript
10分钟学会写Jquery插件实例教程
2014/09/06 Javascript
Angularjs过滤器使用详解
2016/05/25 Javascript
Bootstrap组件系列之福利篇几款好用的组件(推荐)
2016/06/23 Javascript
使用bootstrap typeahead插件实现输入框自动补全之问题及解决办法
2016/07/07 Javascript
Bootstrap 3.x打印预览背景色与文字显示异常的解决
2016/11/06 Javascript
JavaScript实现百度搜索框效果
2020/03/26 Javascript
Koa2微信公众号开发之本地开发调试环境搭建
2018/05/16 Javascript
基于Vue自定义指令实现按钮级权限控制思路详解
2018/05/23 Javascript
在小程序开发中使用npm的方法
2018/10/17 Javascript
vue基础之模板和过滤器用法实例分析
2019/03/12 Javascript
node.js中npm包管理工具用法分析
2020/02/14 Javascript
VUE 单页面使用 echart 窗口变化时的用法
2020/07/30 Javascript
[03:07]DOTA2英雄基础教程 冰霜诅咒极寒幽魂
2013/12/06 DOTA
python中返回矩阵的行列方法
2018/04/04 Python
详解python多线程、锁、event事件机制的简单使用
2018/04/27 Python
一行代码让 Python 的运行速度提高100倍
2018/10/08 Python
详解从Django Rest Framework响应中删除空字段
2019/01/11 Python
如何在VSCode上轻松舒适的配置Python的方法步骤
2019/10/28 Python
Windows 下python3.8环境安装教程图文详解
2020/03/11 Python
无需压缩软件,用python帮你操作压缩包
2020/08/17 Python
高三自我鉴定怎么写
2013/10/19 职场文书
小加工厂管理制度
2014/01/21 职场文书
写求职信有什么意义
2014/02/17 职场文书
社区服务活动感想
2015/08/11 职场文书
Go语言空白表示符_的实例用法
2021/07/04 Golang
mysql 联合索引生效的条件及索引失效的条件
2021/11/20 MySQL