主頁 > 知識庫 > canvas實現(xiàn)有遞增動畫的環(huán)形進度條的實現(xiàn)方法

canvas實現(xiàn)有遞增動畫的環(huán)形進度條的實現(xiàn)方法

熱門標簽:百度競價點擊價格的計算公式 硅谷的囚徒呼叫中心 網(wǎng)站建設 智能手機 美圖手機 使用U盤裝系統(tǒng) 檢查注冊表項 阿里云

老規(guī)矩,直接看圖!

效果如下:

高清大圖!

 碼農(nó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的樣式繪制代碼

這段代碼也很簡單,看canvas的api即可

3-1、vue組件中,script標簽頂部定義需要用的變量

3-2、vue的methos對象中,定義方法三個:

drawBaseCanvas:用來繪制底部灰色圓環(huán)。由于灰色圓環(huán)沒有動畫效果,所以一開始就繪制一個完整的灰色圓環(huán)即可。drawClrCanvas:用來繪制上邊的彩色圓環(huán)。clearCanvas:用來清空畫布。這是彩色圓環(huán)動畫需要。因為我們圓環(huán)動畫效果的核心就是,每隔一段時間就把彩色圓環(huán)清空一下,然后把結(jié)束角度值增大、重畫,這樣連續(xù)起來就是動畫。

以下是三個方法的代碼:

上邊三個方法里邊的代碼,幾乎都是對canvas API的應用,看教程即可。

只有draoClrCanvas方法中,canvas圓形的繪制時,arc的參數(shù)里關(guān)于開始值、結(jié)束值的設置。

開始值決定了圓環(huán)的起始繪制位置,結(jié)束值決定了結(jié)束的位置(我好像說了一句廢話,但是冥思苦想后的思想描述文字,不想刪掉哈哈哈)

這個結(jié)束值的計算,對于我來說還是比較麻煩的。

count變量為什么要這么計算,我也忘了我是怎么鼓搗出來的了。

this.grade是100以內(nèi)的正整數(shù),表示分值。被定義在data中,默認是0分。

所以一開始彩色圓環(huán)就看不見,因為起始點和結(jié)束點都是0點。

如果更改grade的值,從0-100,canvas彩色圓環(huán)的值也就會更改。

這樣,只要我們逐漸修改grade的值,重新繪制,彩色圓環(huán)就會逐漸遞增,實現(xiàn)動畫效果。

圓環(huán)動畫效果

由于我這里需求特殊,需要用戶每次翻到canvas所在swiper時,才會觸發(fā)動畫(后來更麻煩一點需要柱狀圖和canvas部分有個入場效果后,動畫才開始。效果就是上圖中最長的那張gif動畫那樣)。

所以我得借助swiper才能實現(xiàn)。在swiper切換的回調(diào)函數(shù)中,從0開始不停遞增grade分數(shù),并重新觸發(fā)彩色圓環(huán)的繪制,進而實現(xiàn)動畫效果。

vue中我用的swiper是'vue-awesome-swiper'。她的用法我在其他文章中寫過步驟。

swiper在vue-data中的配置里,有一個on對象。在on對象中的slideChange函數(shù),就是每次翻頁swiper時會觸發(fā)的回調(diào)函數(shù)。

這里我說一下幾個比較特殊的點:

(1)vm:是我早就在vue的script中存儲的變量,初始化為null,然后在mounted中,將其賦值為vue實例對象。

初始化數(shù)據(jù)、繪制灰色圓環(huán)

通過這種方法,我在vue實例對象 - data - swiper - 回調(diào)函數(shù)中去拿vue實例對象 - data中的grade和gradeTarget屬性值,并對其進行修改。

ps:我也不知道這么做是不是很傻的一種做法,當時做到這里時是我遇到的一個難題,不知道怎么在swiper的on回調(diào)中獲取vue實例。于是就有了這么曲線救國的方法。如果看官有更好的解決方案,希望可以給我提供一個新的思路,感激不盡哦親

(2)(this.activeIndex == 2 && vm.isStar) || (this.activeIndex == 1 && !vm.isStar)

這里是因為業(yè)務,才這么判斷,可以忽略。

this在swiperChange函數(shù)中指向swiper對象。this.activeIndex是swiper實例的屬性,用官方的話說“返回當前活動塊(激活塊)的索引。”可以理解他指的是當前翻到的是哪一頁,就是當前你所看的swiper-slide的下標。

我因為用戶的身份,會判斷性的決定當前canvas所在swiper前一頁是否展示。 如果不展示就根本不會繪制前一頁,那么相應的當前頁的swiper的下標就會變成(index-1)。

總而言之,當滿足條件、用戶翻到canvas所在swiper頁面后,我就要觸發(fā)if里邊的圓環(huán)繪制邏輯。否則就走到else里初始化數(shù)據(jù)頁面的狀態(tài)、清除定時器暫停動畫、并把彩色圓環(huán)清空

(3)vm.aniShow

在我上篇《純css繪制柱狀圖》里邊說了,柱狀圖的動畫要跟canvas的動畫一起說。因為他們的動畫實現(xiàn)需要配合swiper的切換。說的就是這里的代碼:

vue - data - aniShow屬性變?yōu)閠rue時,div.row就會添加ani這個class類名:

同樣,aniShow為true,progress的高度就會附上自己的目標值,也就是這個progress的實際高度經(jīng)過百分制轉(zhuǎn)化后被賦予給了style屬性的height。

此時,因為progress的transition監(jiān)聽了height變化,就開始有了高度漸增的柱狀圖遞增動畫了。

而ani類名下,progress的transition-delay實現(xiàn)了其高度錯開遞增效果。

可能只看文字描述很晦澀,再看一眼效果:

 (4)彩色圓環(huán)繪制代碼部分

gradeTarget是實際分值,是最終要繪制到的結(jié)果。

grade從0開始,自增到gradeTarget的大小。

這里我沒有直接++vm.grade,我也不知道自己當時咋想的。

if判斷,如果grade遞增到了目標值gradeTarget或者大于目標值,就停止遞增,并讓grade=gradeTarget。屬于臨界值的判斷。在運動功能中,又算碰撞檢測。

反之,不到目標的話,就清除上一次繪制的canvas畫布,在grade遞增變化后重新繪制新的彩色圓環(huán)。

(5)所有這些放到setTimeout中,暫停500毫秒再執(zhí)行,是為了等柱圖和環(huán)圖入場后,在開始繪制圓環(huán)的遞增效果。

其實上邊代碼都是很簡單的邏輯處理,看官們讀一遍代碼應該就差不離了。

新想法:

這個效果是我很久以前做的,今天在整理制作方法的時候,我想到自己代碼的一種優(yōu)化方案:

其實沒必要在定時器里重新調(diào)用彩色圓環(huán)繪制方法。我們直接改的是this.grade屬性,監(jiān)聽這個屬性的改變就好了其實。這樣此屬性在定時器中被修改,圓環(huán)方法就會自動執(zhí)行。

這還是一個想法,還需要我的實踐。

中間文字的遞增效果:

因為grade是每次遞增的分數(shù),所以利用vue的雙向數(shù)據(jù)綁定,直接把grade當作分數(shù)值綁定到對應dom視圖處即可。

最后,圓環(huán)和上邊柱狀圖的動畫結(jié)合,就是animation控制一下動畫延遲即可。很簡單的。

index.vue源碼:

(注,源碼稍作整理,單獨提取。為了完整性也為了保護其他業(yè)務代碼,部分變量名做了修改,可能會和之前截圖中略微不同)

<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基礎值 */
  c = null, //document.getElementById("myCanvas");
  ctx = null, //canvas-2d畫布
  x = 161 / 2 + 1, //圓心坐標
  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)圖分數(shù)
      gradeTarget: 78.54, //實際得分數(shù),可ajax請求數(shù)據(jù)后修改
      isShow: true,//是否展示第二頁swiper
      aniShow: false,//是否開啟柱圖動畫
      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, // 高度設置,占滿設備高度
        resistanceRatio: 0,
        observeParents: true,
        initialSlide: 2 - 1, //設置初始化時,swiper的默認展示頁面,從零開始
        on: {
          slideChange() {
            if (
              (this.activeIndex == 2 && vm.isShow) ||
              (this.activeIndex == 1 && !vm.isShow)
            ) {
              console.log(this.activeIndex, vm.isShow, "繪制動畫");
              setTimeout(function() {
                // 配合展示柱狀圖動畫
                vm.aniShow = true;
                // 定時器不斷觸發(fā)繪制彩色圓環(huán),實現(xiàn)圓環(huán)動畫效果
                timer1 = setInterval(function() {
                  // 中間分數(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)、清除定時器暫停動畫、并把彩色圓環(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繪制
      /* 基礎值 */
      var c = this.$refs.baseCanvas, //document.getElementById("myCanvas");
        // debugger;
        ctx = c.getContext("2d"),
        o = x,
        randius = r;
      /* 默認灰色圓圈 */
      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; // 用漸變進行填充
      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>

 以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

標簽:湖北 山南 煙臺 賀州 通遼 湘潭 黃山 懷化

巨人網(wǎng)絡通訊聲明:本文標題《canvas實現(xiàn)有遞增動畫的環(huán)形進度條的實現(xiàn)方法》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266