8.canvas 简单实现一下环形动图

环形动图效果

image.png

1.gif

写得很糙, 一旦要添加各种细节时,
就会发现结构本身很重要,
驾驭结构还是感觉不清不楚的,
下面的结构,估计过两天就看不懂了,

版本1.0

思路:
如果用canvas?
用ctx.arc 根据不同百分比求出不同角度,
linewidth + strokeStyle 能够完成 渲染
中间总资产 求和 用 filltext
需要一个数据
各项资产的数值.
传进函数中时,
会遍历求出总和,
以及各自的百分比,
根据百分比, 再转换成 弧度,
把该百分比,传入函数,即可完成绘图.
如何实现动画?
canvas 无法通过 css transition , 和animation
可以通过 setInterval
让百分比数值渐变,每次变化都渲染一次,
到达目标数据,就停止动画.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <meta name="viewprot" content="width=device-width, initial-scale=1.0"/>
        <meta http-equiv="X-UA-Compatible" content="ie=edge"/>
        <title>dddd</title>
        <style type="text/css">
          canvas {
            border: 1px solid black;
            margin: 100px;
          }
          .item{
            position: fixed;
            right: 200px;
            top: 200px;
            border: 1px solid black;
            padding: 30px;
            border-radius: 15px;
          }
        </style>
    </head>
    <body>
      
      <canvas id="myCanvas" width="500" height="500"></canvas>
      
         
         <!--这个是用来更改数值,测试的-->
         <div class="item">
           house: <input type="range" min="0" max="1000" value="500"  name="house" id="house" value="" /><br />
           car : <input type="range" min="0" max="1000" value="500" name="car" id="car" value="" /><br />
           money : <input type="range" min="0" max="1000" value="500" name="money" id="money" value="" />
         </div>
         
        <script type="text/javascript">
            var myCanvas = document.getElementById("myCanvas");
            var ctx = myCanvas.getContext("2d");
            // 返回一个随机整数数
            function randomNum (num) {
                return parseInt(Math.random() * num + 1);
            }
            // 返回一个随机颜色
            function randomColor () {
                return 'rgb(' + randomNum(255) +','+randomNum(255)+','+randomNum(255)+')';
            }
            
            // 假设有三个值, 先进行画图.
            // 基本数值
            
            // 假设一个值
            var data = {
            }
            data.house = 55;
            data.money = 21;
            data.car = 77;
            
            
            // 构造函数
            function Data (data,ctx) {
              // data 传入的是基本数据
              // ctx 是要进行作画的 canvas 画笔
              
              // 对传进来的data 进行处理, 放在实例上的data1属性中
              this.createData = function (data) {
                
                this.data1 = {};
                var data1 = this.data1;
                // data1.sum 为总资产, 方便后面求出百分比, 再由百分比换算成角度
                data1.sum = 0;
                for(var key in data) {
                  data1.sum += Number(data[key]);
                }
                for(var key in data){
                  // data1最后生成的结构是这样的
                  //data1 = {
                    //house : {
                      //rad : 1.2PI(数据必要当真),
                      //bgColor : "red"(数据不要当真)
                    //},
                    //...其他属性的结构也一样. 可知, this.createData 方法就是把data的数值稍微处理了一下变成 data1 这种形式,方便作画.
                  //}
                  
                  data1[key] = {};
                  data1[key].rad = data[key] / data1.sum * 2 * Math.PI;
                  if (!data1[key].bgColor) {
                    data1[key].bgColor = randomColor();
                  }
                }
              }
              
              // 也是返回一个要作画的数据data2, 这个数据是通过动画最后要到达的数据. 数据结构跟data1 相同.
              this.targetData = function (data) {
                // data2 为 要到达的 数据.
                this.data2 = new this.createData(data).data1;
                console.log(this.data2);
                return this;
              }
              // 画图
              this.render = function () {
                var beginRad = 0;
                var stopRad = 0;
                var data1 = this.data1;
                // 有几个属性, 我们就画几个圆弧,下一个圆弧的起始角度为前面的圆弧角度的累加,
                // 这样就能在一个圆里完整按照比例呈现.
                for(var key in data1){
                  if (key == "sum") {
                    // 属性里的 sum 不应该参与 作图, 实际上, 数据的结构还是有问题的.
                    continue
                  }
                  // 保证 起点是从上一个的终点
                  beginRad = stopRad;
                  // 保证终点的数值会累加
                  stopRad += data1[key].rad;
                  ctx.beginPath();
                  ctx.arc(250,250,150,beginRad,stopRad,0);
                  ctx.lineWidth = 20;
                  ctx.strokeStyle = data1[key].bgColor;
                  ctx.stroke();
                }
                // 中间总资产 字体的 展示.
                ctx.font = "20px 微软";
                ctx.fillStyle = "#007AFF";
                ctx.textAlign = "center";
                ctx.verticalAlign = "middle";
                ctx.clearRect(160,160,190,190);// 这个地方也许有更简单的解决办法,
                ctx.fillText("总资产为"+data1.sum,255,255);
              }
              
              // 下面是动画相关的部分, 当数值发生变化时, 调用的方法.
              this.change = function (data) {
                // 目标数据转换成 作图数据. 也就是生成data2.
                this.targetData(data);
                //根据data2 重新赋值给data1
                // 作图还是根据data1, 但我们让data1的数据产生变化, 接近 data2
                var self = this;
                var data2 = self.data2;
                var data1 = self.data1;
                data1.sum = data2.sum;
                clearInterval(this.timer);
                // 开启运动(运动就是数值的变化)
                this.timer = setInterval(function () {
                  var flag = true;
                  for(var key in data2) {
                    // 可知 sum这个属性 设置的很不好.
                    if (key != "sum") {
                      // 此处其实是一个兼容处理, 假设变化的值data2, 比原来值data1多出来一个属性,就会出现问题.
                    if (typeof data1[key] == "undefined") {
                      console.log(123);
                        data1[key] = {};
                        data1[key].rad = 0;
                        data1[key].bgColor = data2[key].bgColor;
                    }
                    // 下面这句是, 运动课里学习过的内容.
                    // 保证 data1的值,都向着 data2的值接近
                    // 保证每个data1的值,都足够和data2接近时, 才让 flag 保持 true, 关闭 定时器.关闭运动.
                    if (Math.abs(data2[key].rad * 100 - data1[key].rad * 100) > 1) {
                        data1[key].rad += (data2[key].rad*100 - data1[key].rad*100)/ 700;
                        flag = false;
                    }
                    // 每次data1 更新一次, 我们就重新渲染一次.
                    self.render();
                  }
                    }
                  if (flag) {// 关闭定时器.停止运动.
                    clearInterval(self.timer);
                    console.log("over");
                    console.log(data1);
                  }
                },50);
                
              }
              // 初次渲染获得数据, 以及初始作图.
                this.init = function () {
                    this.createData(data);
                    this.render();
                }
              this.init();
            }
            
            var a = new Data(data,ctx);
            
            
            // 下面的是用来测试.
            var house = document.getElementById("house");
            var car = document.getElementById("car");
            var money = document.getElementById("money");
            
            house.onmouseup = function () {
              data.house = this.value;
              console.log(data);
                a.change(data);
            }
            car.onmouseup = function () {
              data.car = this.value;
              console.log(data);
                a.change(data);
            }
            money.onmouseup = function () {
              data.money = this.value;
              console.log(data);
                a.change(data);
            }
            
            
            //a.change({house:123,money:111,car:156,aaa:777});

            // 能解决一个增加属性的问题,
            // 怎么解决一个减少属性的问题?
            // 对data2进行处理?
            
//            
//            var timer = setInterval(,50);
//              
//          }
            
            
        </script>
    </body>
</html>


今天回顾的时候,

  1. 把注释加上了,这表达能力确实有待提高,
    而且还是需要注释的, 没有注释,我自己都看着费劲.
  2. 把setInterval 先改成 settimeout, 再改成 requestanimationframe
    本来没报什么期待, 也就是想着 回顾一下动画优化 知识点.
    尼玛, 效果非常明显啊, 原来的效果不流畅的,都让我感觉很low
    改完之后, 流畅度上去的不是一点两点啊.
                this.timer = requestAnimationFrame(function () {
                  var flag = true;
                  for(var key in data2) {
                    // 可知 sum这个属性 设置的很不好.
                    if (key != "sum") {
                      // 此处其实是一个兼容处理, 假设变化的值data2, 比原来值data1多出来一个属性,就会出现问题.
                    if (typeof data1[key] == "undefined") {
                      console.log(123);
                        data1[key] = {};
                        data1[key].rad = 0;
                        data1[key].bgColor = data2[key].bgColor;
                    }
                    // 下面这句是, 运动课里学习过的内容.
                    // 保证 data1的值,都向着 data2的值接近
                    // 保证每个data1的值,都足够和data2接近时, 才让 flag 保持 true, 关闭 定时器.关闭运动.
                    if (Math.abs(data2[key].rad * 100 - data1[key].rad * 100) > 1) {
                        data1[key].rad += (data2[key].rad*100 - data1[key].rad*100)/ 700;
                        flag = false;
                    }
                    // 每次data1 更新一次, 我们就重新渲染一次.
                    self.render();
                  }
                    }
                  if (flag) {// 关闭定时器.停止运动.
                    cancelAnimationFrame(self.timer);
                    console.log("over");
                    console.log(data1);
                  } else {
                    console.log(111111);
                    self.change(data);
                  }
                },50);
                
              }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342