您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)canvas如何實(shí)現(xiàn)有遞增動(dòng)畫的環(huán)形進(jìn)度條,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。
效果如下:
高清大圖!
碼農(nóng)多年,老眼昏花,動(dòng)圖看不清?!那就看靜態(tài)截圖?。?!
不同分值效果如下:
看完了賣家秀,我們來看產(chǎn)品的制作過程吧!
canvas繪制圓環(huán)1、vue中,<template lang="pug">里的代碼如下:
canvas#baseCanvas是底部的灰色圓環(huán)
canvas#myCanvas是上邊的彩色圓環(huán)
需要用css樣式幫助我們把彩色圓環(huán)蓋到灰色圓環(huán)上邊。
2、css樣式:
3、js-canvas的樣式繪制代碼
這段代碼也很簡(jiǎn)單,看canvas的api即可
3-1、vue組件中,script標(biāo)簽頂部定義需要用的變量
3-2、vue的methos對(duì)象中,定義方法三個(gè):
drawBaseCanvas:用來繪制底部灰色圓環(huán)。由于灰色圓環(huán)沒有動(dòng)畫效果,所以一開始就繪制一個(gè)完整的灰色圓環(huán)即可。drawClrCanvas:用來繪制上邊的彩色圓環(huán)。clearCanvas:用來清空畫布。這是彩色圓環(huán)動(dòng)畫需要。因?yàn)槲覀儓A環(huán)動(dòng)畫效果的核心就是,每隔一段時(shí)間就把彩色圓環(huán)清空一下,然后把結(jié)束角度值增大、重畫,這樣連續(xù)起來就是動(dòng)畫。
以下是三個(gè)方法的代碼:
上邊三個(gè)方法里邊的代碼,幾乎都是對(duì)canvas API的應(yīng)用,看教程即可。
只有draoClrCanvas方法中,canvas圓形的繪制時(shí),arc的參數(shù)里關(guān)于開始值、結(jié)束值的設(shè)置。
開始值決定了圓環(huán)的起始繪制位置,結(jié)束值決定了結(jié)束的位置(我好像說了一句廢話,但是冥思苦想后的思想描述文字,不想刪掉哈哈哈)
這個(gè)結(jié)束值的計(jì)算,對(duì)于我來說還是比較麻煩的。
count變量為什么要這么計(jì)算,我也忘了我是怎么鼓搗出來的了。
this.grade是100以內(nèi)的正整數(shù),表示分值。被定義在data中,默認(rèn)是0分。
所以一開始彩色圓環(huán)就看不見,因?yàn)槠鹗键c(diǎn)和結(jié)束點(diǎn)都是0點(diǎn)。
如果更改grade的值,從0-100,canvas彩色圓環(huán)的值也就會(huì)更改。
這樣,只要我們逐漸修改grade的值,重新繪制,彩色圓環(huán)就會(huì)逐漸遞增,實(shí)現(xiàn)動(dòng)畫效果。
圓環(huán)動(dòng)畫效果
由于我這里需求特殊,需要用戶每次翻到canvas所在swiper時(shí),才會(huì)觸發(fā)動(dòng)畫(后來更麻煩一點(diǎn)需要柱狀圖和canvas部分有個(gè)入場(chǎng)效果后,動(dòng)畫才開始。效果就是上圖中最長(zhǎng)的那張gif動(dòng)畫那樣)。
所以我得借助swiper才能實(shí)現(xiàn)。在swiper切換的回調(diào)函數(shù)中,從0開始不停遞增grade分?jǐn)?shù),并重新觸發(fā)彩色圓環(huán)的繪制,進(jìn)而實(shí)現(xiàn)動(dòng)畫效果。
vue中我用的swiper是'vue-awesome-swiper'。她的用法我在其他文章中寫過步驟。
swiper在vue-data中的配置里,有一個(gè)on對(duì)象。在on對(duì)象中的slideChange函數(shù),就是每次翻頁swiper時(shí)會(huì)觸發(fā)的回調(diào)函數(shù)。
這里我說一下幾個(gè)比較特殊的點(diǎn):
(1)vm:是我早就在vue的script中存儲(chǔ)的變量,初始化為null,然后在mounted中,將其賦值為vue實(shí)例對(duì)象。
初始化數(shù)據(jù)、繪制灰色圓環(huán)
通過這種方法,我在vue實(shí)例對(duì)象 - data - swiper - 回調(diào)函數(shù)中去拿vue實(shí)例對(duì)象 - data中的grade和gradeTarget屬性值,并對(duì)其進(jìn)行修改。
ps:我也不知道這么做是不是很傻的一種做法,當(dāng)時(shí)做到這里時(shí)是我遇到的一個(gè)難題,不知道怎么在swiper的on回調(diào)中獲取vue實(shí)例。于是就有了這么曲線救國(guó)的方法。如果看官有更好的解決方案,希望可以給我提供一個(gè)新的思路,感激不盡哦親
(2)(this.activeIndex == 2 && vm.isStar) || (this.activeIndex == 1 && !vm.isStar)
這里是因?yàn)闃I(yè)務(wù),才這么判斷,可以忽略。
this在swiperChange函數(shù)中指向swiper對(duì)象。this.activeIndex是swiper實(shí)例的屬性,用官方的話說“返回當(dāng)前活動(dòng)塊(激活塊)的索引?!笨梢岳斫馑傅氖钱?dāng)前翻到的是哪一頁,就是當(dāng)前你所看的swiper-slide的下標(biāo)。
我因?yàn)橛脩舻纳矸?,?huì)判斷性的決定當(dāng)前canvas所在swiper前一頁是否展示。 如果不展示就根本不會(huì)繪制前一頁,那么相應(yīng)的當(dāng)前頁的swiper的下標(biāo)就會(huì)變成(index-1)。
總而言之,當(dāng)滿足條件、用戶翻到canvas所在swiper頁面后,我就要觸發(fā)if里邊的圓環(huán)繪制邏輯。否則就走到else里初始化數(shù)據(jù)頁面的狀態(tài)、清除定時(shí)器暫停動(dòng)畫、并把彩色圓環(huán)清空
(3)vm.aniShow
在我上篇《純css繪制柱狀圖》里邊說了,柱狀圖的動(dòng)畫要跟canvas的動(dòng)畫一起說。因?yàn)樗麄兊膭?dòng)畫實(shí)現(xiàn)需要配合swiper的切換。說的就是這里的代碼:
vue - data - aniShow屬性變?yōu)閠rue時(shí),div.row就會(huì)添加ani這個(gè)class類名:
同樣,aniShow為true,progress的高度就會(huì)附上自己的目標(biāo)值,也就是這個(gè)progress的實(shí)際高度經(jīng)過百分制轉(zhuǎn)化后被賦予給了style屬性的height。
此時(shí),因?yàn)閜rogress的transition監(jiān)聽了height變化,就開始有了高度漸增的柱狀圖遞增動(dòng)畫了。
而ani類名下,progress的transition-delay實(shí)現(xiàn)了其高度錯(cuò)開遞增效果。
可能只看文字描述很晦澀,再看一眼效果:
(4)彩色圓環(huán)繪制代碼部分
gradeTarget是實(shí)際分值,是最終要繪制到的結(jié)果。
grade從0開始,自增到gradeTarget的大小。
這里我沒有直接++vm.grade,我也不知道自己當(dāng)時(shí)咋想的。
if判斷,如果grade遞增到了目標(biāo)值gradeTarget或者大于目標(biāo)值,就停止遞增,并讓grade=gradeTarget。屬于臨界值的判斷。在運(yùn)動(dòng)功能中,又算碰撞檢測(cè)。
反之,不到目標(biāo)的話,就清除上一次繪制的canvas畫布,在grade遞增變化后重新繪制新的彩色圓環(huán)。
(5)所有這些放到setTimeout中,暫停500毫秒再執(zhí)行,是為了等柱圖和環(huán)圖入場(chǎng)后,在開始繪制圓環(huán)的遞增效果。
其實(shí)上邊代碼都是很簡(jiǎn)單的邏輯處理,看官們讀一遍代碼應(yīng)該就差不離了。
新想法:
這個(gè)效果是我很久以前做的,今天在整理制作方法的時(shí)候,我想到自己代碼的一種優(yōu)化方案:
其實(shí)沒必要在定時(shí)器里重新調(diào)用彩色圓環(huán)繪制方法。我們直接改的是this.grade屬性,監(jiān)聽這個(gè)屬性的改變就好了其實(shí)。這樣此屬性在定時(shí)器中被修改,圓環(huán)方法就會(huì)自動(dòng)執(zhí)行。
這還是一個(gè)想法,還需要我的實(shí)踐。
中間文字的遞增效果:
因?yàn)間rade是每次遞增的分?jǐn)?shù),所以利用vue的雙向數(shù)據(jù)綁定,直接把grade當(dāng)作分?jǐn)?shù)值綁定到對(duì)應(yīng)dom視圖處即可。
最后,圓環(huán)和上邊柱狀圖的動(dòng)畫結(jié)合,就是animation控制一下動(dòng)畫延遲即可。很簡(jiǎn)單的。
index.vue源碼:
(注,源碼稍作整理,單獨(dú)提取。為了完整性也為了保護(hù)其他業(yè)務(wù)代碼,部分變量名做了修改,可能會(huì)和之前截圖中略微不同)
<template lang='pug'> .indexs#Indexs.app-bg transition(name="fade") swiper#swiperBox(:options="swiperOption" ref="mySwiper") swiper-slide.swiper-slide1 .container .up swiper-slide.swiper-slide2(v-if="isShow") .my-shark .up swiper-slide.swiper-slide3 .container .data-cont .data.data01 .data01-charts .row(v-for='item,index in Data' :key="index" :class='aniShow ? "ani":""') .data-txt {{item.grade > 0 ? item.grade : '無數(shù)據(jù)'}} .progress(:class='item.grade == 0 ? "nodata" : ""' :style="'height: ' + (aniShow ? (item.grade >= 100 ? (100 * 1.5) / 100 : item.grade == 0 ? 0.04 : item.grade * 1.5 / 100) : 0) +'rem'") span.pg-data .week {{item.week}} .data.data02 .data02-charts .canvas-box //- baseCanvas canvas#baseCanvas.my-canvas(ref="baseCanvas" width="174" height="174") //- canvas canvas#myCanvas.my-canvas.clr-canvas(ref="myCanvas" width="174" height="174") .canvas-data #[span.num {{grade}}]分 </template> <script> var vm = null, timer1 = null, /* canvas基礎(chǔ)值 */ c = null, //document.getElementById("myCanvas"); ctx = null, //canvas-2d畫布 x = 161 / 2 + 1, //圓心坐標(biāo) r = (161 - 10) / 2; //半徑大小 /* swiper組件 */ import { swiper, swiperSlide } from "vue-awesome-swiper"; import { getData } from "../io/getData"; export default { name: "Indexs", components: { swiper, swiperSlide }, data() { return { grade: 0, //圓環(huán)圖分?jǐn)?shù) gradeTarget: 78.54, //實(shí)際得分?jǐn)?shù),可ajax請(qǐng)求數(shù)據(jù)后修改 isShow: true,//是否展示第二頁swiper aniShow: false,//是否開啟柱圖動(dòng)畫 Data:[{ week: "第一周", grade: 0 }, { week: "第二周", grade: 30 }, { week: "第三周", grade: 99.99 }, { week: "第四周", grade: 76.98 }, { week: "第五周", grade: 100 }], swiperOption: { //swiper參數(shù) notNextTick: true, direction: "vertical", grabCursor: true, setWrapperSize: true, autoHeight: true, slidesPerView: 1, mousewheel: false, mousewheelControl: false, height: window.innerHeight, // 高度設(shè)置,占滿設(shè)備高度 resistanceRatio: 0, observeParents: true, initialSlide: 2 - 1, //設(shè)置初始化時(shí),swiper的默認(rèn)展示頁面,從零開始 on: { slideChange() { if ( (this.activeIndex == 2 && vm.isShow) || (this.activeIndex == 1 && !vm.isShow) ) { console.log(this.activeIndex, vm.isShow, "繪制動(dòng)畫"); setTimeout(function() { // 配合展示柱狀圖動(dòng)畫 vm.aniShow = true; // 定時(shí)器不斷觸發(fā)繪制彩色圓環(huán),實(shí)現(xiàn)圓環(huán)動(dòng)畫效果 timer1 = setInterval(function() { // 中間分?jǐn)?shù)文案更改 var num = vm.grade; num++; if (num >= vm.gradeTarget) { vm.grade = vm.gradeTarget; clearInterval(timer1); } else { vm.grade = num; } vm.clearCanvas(); vm.drawClrCanvas(); }, 1000 / 60); }, 500); } else { // 翻頁后,初始化數(shù)據(jù)頁面的狀態(tài)、清除定時(shí)器暫停動(dòng)畫、并把彩色圓環(huán)清空 console.log("其他頁"); clearInterval(timer1); vm.grade = 0; vm.aniShow = false; vm.clearCanvas(); } } } } }; }, computed: {}, mounted() { // 初始化數(shù)據(jù)、繪制灰色圓環(huán) vm = this; c = this.$refs.myCanvas; ctx = c.getContext("2d"); this.drawBaseCanvas(); }, methods: { drawBaseCanvas() { // canvas繪制 /* 基礎(chǔ)值 */ var c = this.$refs.baseCanvas, //document.getElementById("myCanvas"); // debugger; ctx = c.getContext("2d"), o = x, randius = r; /* 默認(rèn)灰色圓圈 */ ctx.strokeStyle = "#eee"; ctx.lineWidth = 10; ctx.beginPath(); ctx.arc(o, o, randius, 0, 2 * Math.PI); ctx.stroke(); }, clearCanvas() { // 清除畫布 ctx.clearRect(0, 0, 200, 200); }, drawClrCanvas() { var gradient = ctx.createLinearGradient(75, 50, 5, 90); gradient.addColorStop("0", "#C88EFF"); gradient.addColorStop("1.0", "#7E5CFF"); ctx.strokeStyle = gradient; // 用漸變進(jìn)行填充 ctx.lineWidth = 10; ctx.lineCap = "round"; ctx.shadowColor = "rgba(191,142,255, 0.36)"; ctx.shadowBlur = 8; ctx.shadowOffsetY = 8; ctx.beginPath(); var count = this.grade / (100 / 2) + 1; ctx.arc(x, x, r, Math.PI, Math.PI * count, false); ctx.stroke(); } } }; </script> <style lang='scss'> // 柱圖 .row { position: relative; z-index: 1; width: 0.61rem; margin-bottom: -0.28 - 0.08 - 0.38rem; text-align: center; } .data-txt { font-size: 0.2rem; line-height: 0.2rem; margin-bottom: 0.09rem; } .progress { height: 0rem; transition: height 0.5s ease-in-out; } .ani { @for $i from 1 to 6 { &:nth-of-type(#{$i}) { .progress { transition-delay: #{$i * 0.15}s; } } } // &:nth-of-type(1) { // .progress { // transition-delay: .4s; // } // } // &:nth-of-type(2) { // .progress { // transition-delay: .8s; // } // } // &:nth-of-type(3) { // .progress { // transition-delay: 1s; // } // } // &:nth-of-type(4) { // .progress { // transition-delay: 1.4s; // } // } // &:nth-of-type(5) { // .progress { // transition-delay: 1.8s; // } // } } .pg-data { display: block; width: 0.12rem; height: 100%; margin: 0 auto; background: linear-gradient(0deg, #c88eff 0%, #7e5cff 100%); box-shadow: 0 -0.04rem 0.14rem 0 rgba(129, 93, 255, 0.4); border-radius: 0.05rem 0.05rem 0 0; } // 0分展示規(guī)則 .nodata { .pg-data { border-radius: 0; background: #e7e7e7; box-shadow: none; } } .week { font-size: 0.2rem; line-height: 0.2rem; margin-top: 0.08rem; color: #666; } // 環(huán)圖 - data02數(shù)據(jù)部分 .data02-charts { margin-top: 0.32rem; height: 1.61rem; } .canvas-box { position: relative; float: left; width: 1.61rem; height: 1.61rem; margin-left: 0.92rem; } .my-canvas { width: 1.61rem; height: 1.61rem; } .clr-canvas { position: absolute; top: 0; left: 0; } .canvas-data { position: absolute; top: 0.56rem; left: 0; right: 0; margin: auto; margin-left: -0.1rem; text-align: center; font-size: 0.24rem; .num { font-size: 0.32rem; font-weight: 600; } } </style>
關(guān)于“canvas如何實(shí)現(xiàn)有遞增動(dòng)畫的環(huán)形進(jìn)度條”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。