大家好!我是猫小白,本期给大家带来一个简略的实战 demo,心愿大家食用得开心,有播种。
== 首先申明:此 demo 存粹为了学习其中的动画常识,请不要用于实在的场景,否则被 JC 叔叔抓起包吃包住就不应该了瑟!==
此我的项目基于 == 成都天府衰弱通 ==,其它地区的场合码不晓得是否有相似的动画,如果没有也没关系,能够学习下是如何实现的。
为啥子要做这个呢?那是因为有一天我进来买菜,回来保安叔叔恩是要我扫一下场合码,作为一个 5 星好公民,必定是十分配合的掏出手机开始扫。叮~,屏幕上呈现一个不挺放大放大的箭头。我就想,要不必代码来实现一下这个动画吧。
于是就有了这篇文章,我一共是实现了 4 种办法:
- 第一种,css,零 js 代码(最简略)
- 第二种,svg,零 js 代码 (较简略)
- 第三种,js+dom (较简单)
- 第四种,js+canvas (较简单)
全文源码地址:点击跳转到 github,能够拉下来亲操练操练,顺便给一个 start!
接下来,咱们一个一个来剖析如何实现的。
筹备工作
首先咱们伪装进来买个菜(未解封的同胞除外例如上海的敌人),回来的时候去保安那里故作镇定,连贯的掏出手机关上天府衰弱通(成都的哈)扫一下,而后给保安亮一下,不退出界面,淡定回家。
回到家后,拿出手机,关上刚刚的界面,截一个图:
退出 html
中,作为 div
容器的背景图片,而后咱们创立一个红色的框把两头的圆圈和勾遮住,因为咱们要本人实现其中的动画成果。
实现成果如下:
遮板的 css 代码:
/** 遮板 **/
.shutter {
position: absolute;
display: block;
width: 120px;
height: 120px;
background-color: #fff;
top: 206px;
left: 150px;
}
外围 html 代码构造如下,:
<body>
<div class="app">
<div class="content">
<!-- 遮板把原来的箭头用红色背景遮住,本人实现箭头 -->
<div class="shutter"></div>
<!-- 本人实现的箭头和圆 -->
<div class="circle"></div>
</div>
</div>
</body>
只是列举要害的代码,残缺代码请到 github 仓库查看。
筹备工作实现,开始正片!
第一种,css,零 js 代码
应用 css3
的animation
动画,能够很容易实现这样的成果,不必一行 js 代码。单要害是要看明确咱们指标动画是由哪些局部组成的,动画是怎么变动的。所以先再次看看这个gif
,留神其中动画局部:
不难发现:
- 圆圈是由小到大,再由大到小
- 内圈放大到肯定水平时,外圈(扩散局部)呈现,并且半径不停增大,当内圈放到最大时,扩散圈隐没
- 外部的勾也会一起放大放大,频率和内圈雷同
依据下面的法则,咱们先实现一个内圈(绿色圈局部)
外部圈 css 代码:
/** 外部圆圈 **/
.circle {
position: absolute;
z-index: 9;
width: var(--circle-size);
height: var(--circle-size);
top: 218px;
left: 165px;
background-color: var(--theme-green);
/* background-color: red; */
border-radius: 45px;
animation: circleMove 0.7s alternate infinite cubic-bezier(1, 1, 1, 1);
}
–circle-size、–theme-green 是咱们在结尾定义的变量,不便后续应用。
:root {
--theme-green: #4ba776; /** 主题绿色 **/
--circle-size: 90px; /** 圆圈大小 宽高一样 **/
}
外部圈画好了,还差一个红色的 ”√”,为了简略我间接应用了 ”√” 这个字符串,退出伪类元素的 content
中。你们也能够用图片或者 svg。
“√” 的 css
代码如下:
/** 外部的勾,应用 `.circle` 的 `before` 伪类 **/
.circle::before {
z-index: 2;//z-index 要比外圈(阔散圈)大,否则要被背景笼罩
position: absolute;
display: block;
content: "✔";
font-size: 63px;
color: #fff;
left: 18px;
top: -1px;
}
让咱们预览下当初的成果:
哟也~ 看起来是那么回事了。
当初咱们依照内圈雷同的色彩和大小,画一个内部圈(扩散圈)。
参考下面的 "√"
的实现,咱们用 after
伪类来实现:
/** 动画时内部突变圈 */
.circle::after {
z-index: 1;//z-index 要比外部的勾小,否则要笼罩掉勾
position: absolute;
display: block;
content: "";
width: calc(var(--circle-size));
height: calc(var(--circle-size));
box-sizing: border-box;
border-radius: 50%;
background-color: #4ba776;
transform: scale(1.2);
/** 外圈动画 一轮工夫是内圈的 2 倍 外部圈放大 0.7s+ 放大 0.7s=1.4s infinite 有限反复 **/
/* animation: outCircleMove 1.4s infinite cubic-bezier(1, 1, 1, 1); */
}
内圈退出放大放大动画,在 .circle
中计入animation
/** 外部圆圈 **/
.circle {
position: absolute;
z-index: 9;
width: var(--circle-size);
height: var(--circle-size);
top: 218px;
left: 165px;
background-color: var(--theme-green);
/* background-color: red; */
border-radius: 45px;
/* 退出放大放大动画 alternate:轮流播放,放大再放小 infinite:有限播放 ease-in-out:变动函数 */
animation: circleMove 0.7s alternate infinite ease-in-out;
}
/** 定义放大放大动画, 最大放大到 1.2 倍 **/
@keyframes circleMove {
0% {transform: scale(1);
}
100% {transform: scale(1.2);
}
}
解读:工夫设置为 0.7s,咱们定义了 circleMove 动画,意思是在 0 秒的时候圆圈放弃 1 倍大小,在 0.1~0.7s 圆圈逐步变为 1.2 倍大小。变动的快慢是由
animation-timing-function
参数决定的,文中设置的ease-in-out
是进入和退出较慢,两头速度较快的一种变动曲线。
咱们能够关上开发者工具,点击animation
属性的曲线图标,查看和抉择想要的动画曲线
好内圈动画曾经实现,咱们来看下成果:
perfect!当初咱们退出外圈动画,让咱们回顾一下:外圈动画是要在内圈放大到肯定工夫外圈才呈现,咱们暂定 1 /5, 也就是 20% 的时候显示并且开始放大(放大对应属性:scale);放大后外圈会越来越便通明,直到内圈放到最大,此时齐全通明,感觉像是扩散的成果(通明对应属性:opacity)。
晓得这些后,咱们设置外圈动画如下:
/* 动画时内部突变圈/
.circle::after {
z-index: 1;
position: absolute;
display: block;
content: "";
width: calc(var(--circle-size));
height: calc(var(--circle-size));
box-sizing: border-box;
border-radius: 50%;
background-color: #4ba776;
/** 外圈动画 一轮工夫是内圈的 2 倍 外部圈放大 0.7s+ 放大 0.7s=1.4s infinite 有限反复 **/
animation: outCircleMove 1.4s infinite ease-in-out;
}
/** 定义扩散圈的放大放大动画 **/
@keyframes outCircleMove {
/**0%~50% 0s~ 0.7s 前的工夫 此时内圈放大,扩散圈期待机会呈现 **/
0% {opacity: 0;}
/**10% 此时内圈曾经放大一段时间,扩散全显示 opacity 设为 1,倍率为 1 **/
10% {
opacity: 1;
transform: scale(1);
}
/**50% 到 100% 是动画 0.7s~1.4s 的工夫 此时内圈放大,扩散圈变为 1.3 倍,然而透明度设为 0 **/
50%,
100% {transform: scale(1.3);
opacity: 0;
}
}
至此就功败垂成了,看下成果:
第二种,svg,零 js 代码 (较简略)
下面了利用 css3 的动画属性,操作起来非常简单。当初介绍一种用 svg 动画的形式实现雷同的成果。咱们晓得 svg 也是有比拟丰盛的动画个性反对。
咱们简略列举一下 svg 实现动画的几种办法:
1.set
set 元素是 SVG 动画元素中最简略的元素。在通过特定工夫距离后,它只是将属性设置为特定值。因而,形态不会间断进行动画解决,而只是更改属性值一次。
2.animate
<animate> 元素通常搁置到一个 SVG 图像元素外面,用来定义这个图像元素的某个属性的动画变动过程。
3.animateMotion
<animateMotion> 元素也是搁置一个图像元素外面,它能够援用一个当时定义好的动画门路,让图像元素按门路定义的形式静止。
4.animateTransform
动画上一个指标元素变换属性,从而使动画管制平移,缩放,旋转或歪斜。
简略理解过后,咱们明天次要用animate
来实现动画。
首先,咱们还是拿出下面的根底布局,祖传代码(背景 + 遮罩)
<div class="app">
<div class="content">
<!-- 遮板把原来的箭头用红色背景遮住,本人实现箭头 -->
<div class="shutter"></div>
<!-- 本人实现的箭头动画 -svg-->
<svg></svg>
</div>
</div>
</body>
根本框架的款式和上一节 css 动画的款式雷同,就不反复粘贴了。
而后咱们画一个圆和勾,也就是外部的局部;svg 中画圆是用<circle>
, 于是咱们有这样的代码:
<div class="app">
<div class="content">
<!-- 遮板把原来的箭头用红色背景遮住,本人实现箭头 -->
<div class="shutter"></div>
<!-- 本人实现的箭头和圆 -->
<svg
class="circle"
width="130"
height="130"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<circle cx="45" cy="45" r="45" fill="#4ba776"></circle>
</g>
</svg>
</div>
</div>
</body>
成果如下:
持续增加两头的红色 "√"
。为了简介,咱们用<text>
文字标签:
增加 <text>
后的代码如下:
<div class="app">
<div class="conten">
<!-- 遮板把原来的箭头用红色背景遮住,本人实现箭头 -->
<div class="shutter"></div>
<!-- 本人实现的箭头和圆 -->
<svg
class="circle"
width="130"
height="130"
xmlns="http://www.w3.org/2000/svg"
>
<g>
<circle cx="65" cy="65" r="45" fill="#4ba776"></circle>
<!--text 增加 -->
<text font-size="63" fill="#fff" y="90" x="38">✔</text>
</g>
</svg>
</div>
</div>
</body>
增加文字后成果如下:
好了,内圈增加好了,咱们再增加一个外圈(扩散圈),还是用<circle>
,大小和地位都和内圈截然不同,这里就不粘贴代码了。
当初来增加动画,依据上一章节的 css3 动画,咱们须要不挺的放大放大内圈的大小。所以咱们通过动画改编圆的半径就 ok 了。下面介绍了 <animate>
标签能够动静扭转某一属,这里的属性就是半径R
:
... 其它代码
<circle cx="65" cy="65" r="45" fill="#4ba776">
<!-- 增加到 circle 标签外部,指定 attributeName 为半径 r,values 是半径变动的值,咱们让它先变大再变小,dur 是动画工夫 repeatCount:indefinite 有限循环 -->
<animate
attributeName="r"
values="45;54;45"
dur="1.4s"
repeatCount="indefinite"
/>
</circle>
... 其它代码
实现成果:
完满! 看起来就是那么回事,前面的步骤其实都是依葫芦画瓢。
增加外部 "√"
的动画,用 <animate>
扭转font-size
。
<text font-size="63" fill="#fff" y="90" x="38">
✔
<animate
attributeName="font-size"
values="63;69;63"
dur="1.4s"
repeatCount="indefinite"
/>
</text>
增加后成果咱们来看下:
最初,咱们增加扩散成果。扩散成果其实分两步,第一步是放大半径,另一步是从不通明到通明的过程。所以咱们用 <animate>
扭转半径的同时,扭转它的透明度。
... 其它代码
<!-- 扩散圈 --->
<circle cx="65" cy="65" r="45" fill="#4ba776">
<!-- 设置放大放大动画 --->
<animate
attributeName="r"
values="45;50;65;65;50;45"
dur="1.4s"
repeatCount="indefinite"
/>
<!-- 设置透明度变动动画 --->
<animate
attributeName="opacity"
values="1;1;0.2;0;0;0"
dur="1.4s"
repeatCount="indefinite"
/>
</circle>
... 其它代码
最终成果如下:
第三种,js+dom (较简单)
后面都是通过 css 或者 svg 自带个性实现的动画成果,上面这种是通过 js 动静扭转圆圈 div
大小实现的,较为简单,了解起来须要肯定的 js 根底,在做我的项目优先选择 css3,咱们一起来看下是如何实现的。
首先,咱们还是拿出下面的根底布局,增加内外圈的 div 和布局。
要害 css 局部:
/** 内部圆圈 **/
.circle,
.outCircle {
z-index: 1;
position: absolute;
width: var(--circle-size);
height: var(--circle-size);
top: 218px;
left: 165px;
background-color: var(--theme-green);
/* background-color: red; */
border-radius: 45px;
}
/** 外部的勾 **/
.circle::before {
position: absolute;
display: block;
content: "✔";
font-size: 63px;
color: #fff;
left: 18px;
top: -1px;
}
html 局部:
<body>
<div class="app">
<div class="content">
<!-- 遮板把原来的箭头用红色背景遮住,本人实现箭头 -->
<div class="shutter"></div>
<!-- 本人实现的箭头和圆 -->
<div class="circle"></div>
<div class="outCircle"></div>
</div>
</div>
</body>
至此,元素曾经画好了,就差动画局部了,简略剖析一波:
内圈须要逐步放大再逐步放小,咱们用一个变量 size_inner
来代表放大放大的倍数,STEP_INNER
代表内圈递增或者递加的步长,最终设置到 style
上的 transform
属性。
外圈也须要有一个放大过程,且放大的速度比内圈快,咱们用 size_outer
来代表放大倍数,STEP_OUT
代表外圈递增步长。
外圈须要一个透明度逐步小的值 opacity_out
,透明度变动的步长用OPA_STEP
代替,最终设置 style
上的 opacity
属性。
动画是继续一直的,所以咱们须要不停的执行,先临时采纳 setInterval
函数;
放大到肯定倍数而后再放大,最大倍数咱们用 MAX_SCALE
常量代表,如果递增到最大而后 STEP_INNER
设置为正数,意味着开始放大。
最终的源代码如下:
<script>
const circle = document.querySelector(".circle");
const outCircle = document.querySelector(".outCircle");
let size_inner = 1.0; // 内圈的放大倍数
let size_outer = 1.0; // 内圈的放大倍数
let STEP_INNER = 0.009; // 内圈的递增步值
let STEP_OUT = STEP_INNER + 0.009; // 外圈要比内圈扩散倍速大一点
let opacity_out = 1; // 外圈透明度
const OPA_STEP = 0.03;
const MAX_SCALE = 1.4;
function randerInner() {
// 放大过程
if (STEP_INNER >= 0) {if (size_inner <= MAX_SCALE) {
size_inner += STEP_INNER;
// 管制外圈光环 --start
if (size_inner >= 1.1) {
// 内圈放大到 1.1 倍 外圈光环才呈现,大小和内圈统一,透明度为 1
if (opacity_out === 0) {
opacity_out = 1;
size_outer = size_inner;
}
size_outer += STEP_OUT;
opacity_out -= OPA_STEP;// 透明度逐步
}
// 管制外圈光环 --end
} else {
// 放大到 MAX_SCALE 倍了 从新放大
size_inner = MAX_SCALE;
STEP_INNER = STEP_INNER * -1; // 步长乘以 -1,开始方方向变动
// 管制外圈光环 - 暗藏
opacity_out = 0;
}
} else {
// 放大过程
if (size_inner > 1) {size_inner += STEP_INNER;} else {
// 缩到 1 倍了 从新放大
STEP_INNER = STEP_INNER * -1; // 步长乘以 -1,开始方方向变动
size_inner = 1;
}
}
circle.style.transform = `scale(${size_inner})`; // 设这内圈放大款式
outCircle.style.transform = `scale(${size_outer})`; // 设这内圈放大款式
outCircle.style.opacity = opacity_out; // 设这内圈透明度
}
setInterval(() => {randerInner();
}, 17);
</script>
因篇幅无限,不逐个解释各行代码的含意,有不了解的能够私聊我。
最终成果:
家喻户晓,因为浏览器的 eventloop
机制,setInterval
往往并不能依照设置的工夫执行,在略微简单一些的页面中会呈现卡顿。固可采纳 requestAnimationFrame
api 代替。
代码如下:
<script>
const circle = document.querySelector(".circle");
const outCircle = document.querySelector(".outCircle");
let size_inner = 1.0; // 内圈的放大倍数
let size_outer = 1.0; // 内圈的放大倍数
let STEP_INNER = 0.009; // 内圈的递增步值
let STEP_OUT = STEP_INNER + 0.009; // 外圈要比内圈扩散倍速大一点
let opacity_out = 1; // 外圈透明度
const OPA_STEP = 0.03;
const MAX_SCALE = 1.4;
(function randerInner() {
// 放大过程
if (STEP_INNER >= 0) {if (size_inner <= MAX_SCALE) {
size_inner += STEP_INNER;
// 管制外圈光环 --start
if (size_inner >= 1.1) {
// 内圈放大到 1.2 倍 外圈光环才呈现大小统一
if (opacity_out === 0) {
opacity_out = 1;
size_outer = size_inner;
}
size_outer += STEP_OUT;
opacity_out -= OPA_STEP;
}
// 管制外圈光环 --end
} else {
// 放大到 MAX_SCALE 倍了 从新放大
size_inner = MAX_SCALE;
STEP_INNER = STEP_INNER * -1;
// 管制外圈光环 - 暗藏
opacity_out = 0;
}
} else {
// 放大过程
if (size_inner > 1) {size_inner += STEP_INNER;} else {
// 缩到 1 倍了 从新放大
STEP_INNER = STEP_INNER * -1;
size_inner = 1;
}
}
circle.style.transform = `scale(${size_inner})`; // 设这内圈放大款式
outCircle.style.transform = `scale(${size_outer})`; // 设这内圈放大款式
outCircle.style.opacity = opacity_out; // 设这内圈透明度
requestAnimationFrame(() => {randerInner();
_test();});
})()
</script>
第四种,js+canvas (较简单)
通过下面章节的洗礼,canvas 版本和 js+dom 版本十分类似,惟一的不同就是绘画圆或者文字的 api 不同。
咱们剖析下要点:
- 须要在画布上用
arc
api 绘制 2 个圆,一个内圈一个外圈,并且不停扭转圆圈的大小或透明度。 - 须要用
fillText
绘制一个✔
字样,并且扭转字体大小。 - 须要不停执行绘制函数,这里应用晦涩的
requestAnimationFrame
api。 - 每次绘制前,需清空画布
clearRect
。
其实原理和第三种差不多,都须要用变量记录外部圈变动的大小,到最大值后方向递加。内部圈是须要等到内圈放大到肯定倍数(1.1)倍,才显示进去,并且放大速度要比内圈快,随着外圈变大,透明度逐步减小到 0。
我想第四种和第三种其实都有这样的逻辑,为此我花了一点事件把这个动作进行了封装,采纳了一种 订阅公布模式
来实现。
为了大家浏览我这篇文章的同时,除了能学习到动画常识,也能学习到一些其它的比拟重要的 js 常识。大家应该理解或者听过 js 的各种模式,什么工厂模式、单列模式、观察者模式、公布订阅模式等等,都是学习到了理论知识。明天我就用这个例子介绍下公布订阅模式是如何在实践中应用的。
刚咱们剖析了 canvas
实现的形式和 js+dom
的形式,这两种形式都须要用变量来记录一个值(这里是放大倍数),而且每次执行渲染函数时都要递增一个常量值(成为步长),达到某个最大值(比方 1.3 倍大小)后开始反向递加(步长乘以 -1),而后递加到最后值(1 倍大小)。同时还有一个细节,当内圈递增到肯定倍数(比方 1.1 倍)要让外圈开始退出动画,当内圈递增到最大时,外圈要隐没(透明度设置为 0)
基于这个剖析,咱们得出封装的类须要设计成这样:
/***
* 放大放大数字变动类
* 依据放大放大初始值和步长等递增
* 提供到达某一数值时的回调
* @params {Number} from 数字初始值
* @params {Number} to 数字最大值
* @params {Number} step 数字步长
* @params {Array} dingNums 达到 dingNums 的区间触发回调 只再放大轮回触发
*/
export default class MoveCrycle {constructor({ from = 0, to = 100, step = 1, dingNums = [] }) {
this.from = from;
this.to = to;
this.step = step;
this.dingNums = dingNums;
this.currValue = this.from;
this._turnNum = 0; // 放大放大轮次 一次放大一次放大为一轮
this._event = {onchange: [], // 每一次变动触发的回调
onlarge: [], // 每次到最大值触发的回调
onsmall: [], // 每次到最小值触发的回调
onding: [], // 每次到 dingNums 区间值时触发的回调};
this._init();}
_init() {
// 注册放大事件
// 记录轮次 每次放大 +1
this.on("onlarge", function turn() {this._turnNum++;});
}
// 开始滚动数字
jump() {if (this.step > 0) {
// 放大过程
if (this.currValue <= this.to) {
// 递增过程中
this.currValue += this.step;
// 判断是否在 dingNums 区间
if (this.currValue >= this.dingNums[0] &&
this.currValue <= this.dingNums[1]
) {this._emit("onding", this.currValue, this._turnNum);
}
} else {this._emit("onsmall", this.currValue);
this.currValue = this.to;
this.step *= -1;
}
} else {
// 放大过程
if (this.currValue > this.from) {this.currValue += this.step;} else {
// 缩到初始值了 从新放大
this.step *= -1;
this.currValue = this.from;
this._emit("onlarge", this.currValue);
}
}
this._emit("onchange", this.currValue, this.step > 0, this._turnNum);
}
// 触发事件函数
_emit(type, ...args) {
// 触发每次变动函数
this._event[type].forEach((f) => {
try {f.apply({}, args);
} catch (e) {console.error(e);
}
});
}
// 注册回调
on(type, func) {
if (this._event[type] &&
!this._event[type].filter((f) => f === func).length
) {this._event[type].push(func);
}
}
// 登记回调
off(type, func) {if (this._event[type]) {this._event[type] = this._event[type].filter((f) => f !== func);
}
}
}
一看比拟长,别急缓缓剖析:
constructor
局部就是接支出参,没有什么好说的。须要留神的是this._init()
, 初始化时本人也注册一个onlarge
事件,来记录每次轮回的次数_turnNum
。jump
是外围代码,次要治理每一次被执行时的递增或递加状态,同时通过 this.emit()函数触发响应的回调事件。放大过程中判断是否达到 dingNums 的区间,如果在其中则不停触发onding
事件,当达到最大值时触发onsmall
事件代表行将变小,同时this.step *= -1
步长取反方向值。达到最小值时触发onlarge
事件,代表行将增大,同时this.step *= -1
步长取反方向值。jump
函数并没有这个类中进行递归调用,是因为把这个调用权交给使用者会更加灵便,前面会介绍如何调用。_emit
函数是触发事件的控制中心,依据传入的type
类型找到事件对象中对应的函数数组,遍历执行。用try{} catch(e){}
是为了防止在某个回调函数中出错而影响了其它回调函数失常执行。on
和off
顾名思义,就是注册和登记回调的入口。on
函数把传入的事件类型和函数退出对应的数组中,如果反复增加同一个函数是有效的。off
函数接管事件类型和函数,而后数组中过滤掉。这里留给你想像一下,如果用匿名函数注册的事件能勾销掉吗?
好了,MoveCrycle 类封装好了,回到咱们的主题,看下如何在这个箭头动画中应用。
HTML 局部
<body>
<div class="app">
<div class="content">
<!--canvas 替换箭头局部 -->
<canvas
id="canvas"
class="canvas"
width="140px"
height="140px"
></canvas>
</div>
</div>
</body>
script 局部
引入 MoveCrycle
类,初始化
import MoveCrycle from "./js/MoveCrycle.js";
let cavas = document.getElementById("canvas");
var ctx = cavas.getContext("2d");
// 定义的一些常量和变量上面会用到
const R_SMALL = 45;// 内圈初始大小
... 省略局部代码
// 看着里!!!!初始化封装的类
let move = new MoveCrycle({
from: R_SMALL, // 初始值
to: R_SMALL * 1.35, // 最大 1.35 倍
step: 0.3, // 步长
dingNums: [R_SMALL * 1.1, R_SMALL * 1.35], // 外圈开始呈现的区间内圈的 1.1 倍到 1.35 倍
});
...
注册 onchange
事件,监听每一次变动,而后渲染画布
// 注册数值变动事件,每次变动获取值渲染
move.on("onchange", (value, step) => {
// 绘制内圆
clear(); // 清空画布
// 绘制内圈
drawInnerCircle(value);
// 绘制外圈
drawOutCircle();
// 文字放在最初,不然会被下面的圆属性 fillStyle 笼罩
drawText(value);
});
注册 onding
事件,管制外圈的变大,透明度减小
// 再设定的区间触发事件,管制外圈的变大,透明度减小
move.on("onding", (value) => {
r_out += R_OUT_STEP; // 外圈变大
opa -= OPA_STEP; // 外圈变大的同时透明度减小
});
注册 onlarge
事件,管制外圈
// 动画圈开始变大管制外圈
move.on("onlarge", (value) => {
opa = 1; // 开始变大,透明度为 1
r_out = 45; // 内部开始变大时半径回复到初始值
});
注册 onsmall
事件,管制外圈透明度为 0
move.on("onsmall", (value) => {
// 管制外圈光环 - 暗藏
opa = 0;
});
绘制内圈、外圈、文字函数
// 绘制内圆
function drawInnerCircle(val) {ctx.beginPath();
ctx.arc(SIZE / 2, SIZE / 2, val, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${COLOR}, 1)`;
ctx.fill();}
// 绘制外圈圆 变大的同时透明度降落
function drawOutCircle() {ctx.beginPath();
ctx.arc(SIZE / 2, SIZE / 2, r_out, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${COLOR}, ${opa})`;
ctx.fill();}
// 放大字体 大小随内圈一起变动
function drawText(val) {
//font 放大倍数和内圈一样
ctx.font = (val / R_SMALL) * 60 + "px Arial";
ctx.textBaseline = "center"; // 设置字体底线居中
ctx.textAlign = "center"; // 设置字体对齐形式居中
ctx.fillStyle = `rgba(255,255,255,1)`;
ctx.fillText("✔", 70, 90);
}
革除画布
// 革除画布
function clear() {ctx.clearRect(0, 0, SIZE, SIZE);
}
循环递归函数
// 循环递归函数
(function start() {move.jump(); // 开始执行
requestAnimationFrame(() => {start();
});
})
script 局部残缺代码:
<script type="module">
import MoveCrycle from "./js/MoveCrycle.js";
let cavas = document.getElementById("canvas");
var ctx = cavas.getContext("2d");
const COLOR = "75, 167, 118";
const R_SMALL = 45;// 内圈初始大小
const SIZE = 140; // 画布宽高
const R_OUT_STEP = 0.3 * 2.5; // 外圈变动系数
const OPA_STEP = 0.028;
let r_out = R_SMALL; // 外圈半径累加值
let opa = 1; // 外圈透明度累加值
let move = new MoveCrycle({
from: R_SMALL, // 初始值
to: R_SMALL * 1.35, // 最大 1.35 倍
step: 0.3, // 步长
dingNums: [R_SMALL * 1.1, R_SMALL * 1.35], // 外圈开始呈现的区间内圈的 1.1 倍到 1.35 倍
});
// 注册数值变动事件,每次变动获取值渲染
move.on("onchange", (value, step) => {
// 绘制内圆
clear(); // 清空画布
// 绘制内圈
drawInnerCircle(value);
// 绘制外圈
drawOutCircle();
// 文字放在最初,不然会被下面的圆属性 fillStyle 笼罩
drawText(value);
});
// 再设定的区间触发事件
move.on("onding", (value) => {
r_out += R_OUT_STEP; // 外圈变大
opa -= OPA_STEP; // 外圈变大的同时透明度减小
});
// 动画圈开始变大
move.on("onlarge", (value) => {
opa = 1; // 开始变大,透明度为 1
r_out = 45; // 开始变大时半径回复到初始值
});
// 动画圈开始变小
move.on("onsmall", (value) => {
// 管制外圈光环 - 暗藏
opa = 0;
});
// 绘制内圆
function drawInnerCircle(val) {ctx.beginPath();
ctx.arc(SIZE / 2, SIZE / 2, val, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${COLOR}, 1)`;
ctx.fill();}
// 绘制外圈圆 变大的同时透明度降落
function drawOutCircle() {ctx.beginPath();
ctx.arc(SIZE / 2, SIZE / 2, r_out, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${COLOR}, ${opa})`;
ctx.fill();}
// 放大字体 大小随内圈一起变动
function drawText(val) {
//font 放大倍数和内圈一样
ctx.font = (val / R_SMALL) * 60 + "px Arial";
ctx.textBaseline = "center"; // 设置字体底线居中
ctx.textAlign = "center"; // 设置字体对齐形式居中
ctx.fillStyle = `rgba(255,255,255,1)`;
ctx.fillText("✔", 70, 90);
}
// 循环递归函数
(function start() {move.jump(); // 开始执行
requestAnimationFrame(() => {start();
});
})
// 革除画布
function clear() {ctx.clearRect(0, 0, SIZE, SIZE);
}
// 开始函数
start();
</script>
以上。
残缺代码能够到我的 github 仓库获取!
各位大佬,不要忘了给我 点赞
+ 评论
+ 珍藏
。