Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产小视频在线免费,最近最新在线中文字幕,在线免费自拍

          整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          手把手教你D3.js 實現(xiàn)數(shù)據(jù)可視化極速上手到Vue應(yīng)用



          D3近年來一直是JavaScript最重要的數(shù)據(jù)可視化庫之一,在創(chuàng)建者M(jìn)ike Bostock的維護(hù)下,前景依然無量,至少現(xiàn)在沒有能打的:

          • D3與眾多其他庫的區(qū)別在于無限定制的能力(直接操作SVG)。
          • 它的底層API提供對原生SVG元素的直接控制,但它也帶來了高學(xué)習(xí)曲線的成本。
          • 我們將把D3和Vue結(jié)合在一起 - 使用Vue的動態(tài)數(shù)據(jù)綁定,清晰的語法和模塊化結(jié)構(gòu),可以充分發(fā)揮D3的最佳性能。 根據(jù)廣泛定義,D3可拆分為以下幾種分庫:



          1. 絕大部分的D3課程或書籍,都會著重講解在其DOM操作功能上,但這明顯與近幾年來的web框架理念相違背。
          2. 用于數(shù)據(jù)可視化的D3,其核心在于使用繪圖指令裝飾數(shù)據(jù),從源數(shù)據(jù)創(chuàng)建新的可繪制數(shù)據(jù),生成SVG路徑以及從數(shù)據(jù)和方法在DOM中創(chuàng)建數(shù)據(jù)可視化元素(如軸)的功能。
          3. 有許多用于管理DOM的工具,所有這些工具都可以在D3中集成數(shù)據(jù)可視化功能。這也是D3能與Vue無縫結(jié)合的原因之一。

          于此,我們不需要從D3 DOM操作功能開始學(xué)起,直接通過實例來入門D3。

          1. D3.js 漸進(jìn)入門

          以下實例的模版均為以下形式:

          <html>
              <head>
                  <link rel="stylesheet" href="index.css">
                  <title>Learn D3.js</title>
              </head>
              <body>
                  <!--或其它標(biāo)簽-->
                  <h1>First heading</h1>
                  
                  <script src="https://d3js.org/d3.v4.min.js"></script>
                  <script src="index.js"></script>
              </body>
          </html>
          復(fù)制代碼

          1. 選擇和操作



          你需要學(xué)習(xí)的第一件事是如何使用D3.js選擇和操作DOM元素。該庫在操作DOM方面實際上非常強(qiáng)大,因此理論上可以將其用作jQuery的替代品。以下代碼請逐行添加運(yùn)行。

          // index.js
          d3.select();
          d3.selectAll();
          
          d3.select('h1').style('color', 'red')
          .attr('class', 'heading')
          .text('Updated h1 tag');
          
          d3.select('body').append('p').text('First Paragraph');
          d3.select('body').append('p').text('Second Paragraph');
          d3.select('body').append('p').text('Third Paragraph');
          
          d3.selectAll('p').style('')
          復(fù)制代碼

          2.數(shù)據(jù)加載和綁定


          當(dāng)你要創(chuàng)建可視化時,了解如何加載數(shù)據(jù)以及將其綁定到DOM非常重要。所以在這個實例中,你將學(xué)到這兩點。


          let dataset = [1, 2, 3, 4, 5];
          
          d3.select('body')
              .selectAll('p')
              .data(dataset)
              .enter()
              .append('p') // appends paragraph for each data element
              .text('D3 is awesome!!');
              //.text(function(d) { return d; });
          復(fù)制代碼

          3.創(chuàng)建一個簡單的柱狀圖



          首先需要添加一個svg標(biāo)簽

          <h1>Bar Chart using D3.js</h1>
          
          <svg class="bar-chart"></svg>
          復(fù)制代碼

          然后在index.js中添加(已添加關(guān)鍵注釋):

          // 數(shù)據(jù)集
          let dataset = [80, 100, 56, 120, 180, 30, 40, 120, 160];
          // 定義svg圖形寬高,以及柱狀圖間距
          let svgWidth = 500, svgHeight = 300, barPadding = 5;
          // 通過圖形計算每個柱狀寬度
          let barWidth = (svgWidth / dataset.length);
          
          // 繪制圖形
          let svg = d3.select('svg')
              .attr("width", svgWidth)
              .attr("height", svgHeight);
          
          // rect,長方形
          // 文檔:http://www.w3school.com.cn/svg/svg_rect.asp
          
          let barChart = svg.selectAll("rect")
              .data(dataset) //綁定數(shù)組
              .enter() // 指定選擇集的enter部分
              .append("rect") // 添加足夠數(shù)量的矩形
              .attr("y", d => svgHeight - d ) // d為數(shù)據(jù)集每一項的值, 取y坐標(biāo)
              .attr("height", d => d) // 設(shè)定高度
              .attr("width", barWidth - barPadding) // 設(shè)定寬度
              .attr("transform", (d, i) =>  {
                  let translate = [barWidth * i, 0]; 
                  return "translate("+ translate +")";
              }); // 實際是計算每一項值的x坐標(biāo)
          復(fù)制代碼

          4. 在圖形上方顯示數(shù)值


          這時就需要在上述代碼中創(chuàng)建svg的 text文本


          let text = svg.selectAll("text")
              .data(dataset)
              .enter()
              .append("text")
              .text(d => d)
              .attr("y", (d, i) => svgHeight - d - 2)
              .attr("x", (d, i) =>  barWidth * i)
              .attr("fill", "#A64C38");
          復(fù)制代碼

          過程比較簡單,就是返回文本,計算x/y坐標(biāo),并填充顏色。

          5. scales: 比例尺函數(shù)

          D3中有個重要的概念就是比例尺。比例尺就是把一組輸入域映射到輸出域的函數(shù)。映射就是兩個數(shù)據(jù)集之間元素相互對應(yīng)的關(guān)系。比如輸入是1,輸出是100,輸入是5,輸出是10000,那么這其中的映射關(guān)系就是你所定義的比例尺。

          D3中有各種比例尺函數(shù),有連續(xù)性的,有非連續(xù)性的,在本例子中,你將學(xué)到d3.scaleLinear() ,線性比例尺

          5.1 d3.scaleLinear(),線性比例尺

          使用d3.scaleLinear()創(chuàng)造一個線性比例尺,其中:

          • domain()是輸入域
          • range()是輸出域
          • 相當(dāng)于將domain中的數(shù)據(jù)集映射到range的數(shù)據(jù)集中。
          let scale = d3.scaleLinear().domain([1,5]).range([0,100])
          復(fù)制代碼

          映射關(guān)系:



          值得注意的是,上述代碼只是定義了一個映射規(guī)則,映射的輸入值并不局限于domain()中的輸入域。

          scale(1) // 輸出:0
          scale(4) // 輸出:75
          scale(5) // 輸出:100
          scale(-1) // 輸出:-50
          scale(10) // 輸出:225
          復(fù)制代碼

          于是我們來改造3~4的例子:

          let dataset = [1,2,3,4,5];
          
          let svgWidth = 500, svgHeight = 300, barPadding = 5;
          let barWidth = (svgWidth / dataset.length);
          
          
          let svg = d3.select('svg')
              .attr("width", svgWidth)
              .attr("height", svgHeight);
              
          let yScale = d3.scaleLinear()
              .domain([0, d3.max(dataset)])
              .range([0, svgHeight]);
                  
          let barChart = svg.selectAll("rect")
              .data(dataset)
              .enter()
              .append("rect")
              .attr("y", d => svgHeight - yScale(d))
              .attr("height", d => yScale(d))
              .attr("width", barWidth - barPadding)
              .attr("transform", (d, i) => {
                  let translate = [barWidth * i, 0]; 
                  return "translate("+ translate +")";
              });
          復(fù)制代碼

          然后就會得到以下圖形:



          6. Axes:軸



          軸是任何圖表的組成部分,本例子中將會用到上面講到的比例尺函數(shù)。

          let data= [80, 100, 56, 120, 180, 30, 40, 120, 160];
          
          let svgWidth = 500, svgHeight = 300;
          
          let svg = d3.select('svg')
              .attr("width", svgWidth)
              .attr("height", svgHeight);
          
          // 首先是拿最大值構(gòu)建x軸坐標(biāo)
          let xScale = d3.scaleLinear()
              .domain([0, d3.max(data)])
              .range([0, svgWidth]);
                   
          // 接下來是反轉(zhuǎn)值,用作y軸坐標(biāo)。
          let yScale = d3.scaleLinear()
              .domain([0, d3.max(data)])
              .range([svgHeight, 0]);
          
          // 橫軸的API使用
          let x_axis = d3.axisBottom()
              .scale(xScale);
              
          // 縱軸的API使用
          let y_axis = d3.axisLeft()
              .scale(yScale);
              
          // 在svg中提供了如g元素這樣的將多個元素組織在一起的元素。
          // 由g元素編組在一起的可以設(shè)置相同的顏色,可以進(jìn)行坐標(biāo)變換等,類似于Vue中的 <template>
          
          svg.append("g")
              .attr("transform", "translate(50, 10)")
              .call(y_axis);
                   
          let xAxisTranslate = svgHeight - 20;
                   
          svg.append("g")
              .attr("transform", "translate(50, " + xAxisTranslate  +")")
              .call(x_axis);
          復(fù)制代碼

          7. 創(chuàng)建簡易的SVG元素


          在這里面,你會創(chuàng)建<rect>,<circle>和<line>元素


          let svgWidth = 600, svgHeight = 500;
          let svg = d3.select("svg")
              .attr("width", svgWidth)
              .attr("height", svgHeight)
              .attr("class", "svg-container")
              
          let line = svg.append("line")
              .attr("x1", 100)
              .attr("x2", 500)
              .attr("y1", 50)
              .attr("y2", 50)
              .attr("stroke", "red");
              
          let rect = svg.append("rect")
              .attr("x", 100)
              .attr("y", 100)
              .attr("width", 200)
              .attr("height", 100)
              .attr("fill", "#9B95FF");
              
          let circle = svg.append("circle")
              .attr("cx", 200)
              .attr("cy", 300)
              .attr("r", 80)
              .attr("fill", "#7CE8D5");
          復(fù)制代碼

          8. 創(chuàng)建餅圖



          let data = [
              {"platform": "Android", "percentage": 40.11}, 
              {"platform": "Windows", "percentage": 36.69},
              {"platform": "iOS", "percentage": 13.06}
          ];
          
          let svgWidth = 500, svgHeight = 300, radius =  Math.min(svgWidth, svgHeight) / 2;
          let svg = d3.select('svg')
              .attr("width", svgWidth)
              .attr("height", svgHeight);
          
          //Create group element to hold pie chart    
          let g = svg.append("g")
              .attr("transform", "translate(" + radius + "," + radius + ")") ;
          
          // d3.scaleOrdinal() 序數(shù)比例尺
          // schemeCategory10, 顏色比例尺
          // D3提供了一些顏色比例尺,10就是10種顏色,20就是20種:
          let color = d3.scaleOrdinal(d3.schemeCategory10);
          
          let pie = d3.pie().value(d => d.percentage);
          
          let path = d3.arc()
              .outerRadius(radius)
              .innerRadius(0);
           
          let arc = g.selectAll("arc")
              .data(pie(data))
              .enter()
              .append("g");
          
          arc.append("path")
              .attr("d", path)
              .attr("fill", d => color(d.data.percentage));
                  
          let label = d3.arc()
              .outerRadius(radius)
              .innerRadius(0);
                      
          arc.append("text")
              .attr("transform",  d => `translate(${label.centroid(d)})`)
              .attr("text-anchor", "middle")
              .text(d => `${d.data.platform}:${d.data.percentage}%`);
          復(fù)制代碼

          9. 創(chuàng)建折線圖



          最后,你將學(xué)習(xí)如何創(chuàng)建折線圖以顯示近四個月的比特幣價格。要獲取數(shù)據(jù),你將使用外部API。這個項目還將你在整個課程中學(xué)到的很多概念結(jié)合在一起,所以這是一個很好的可視化課程結(jié)束。

          // 外部API,注意日期記得補(bǔ)零
          const api = 'https://api.coindesk.com/v1/bpi/historical/close.json?start=2019-03-31&end=2019-07-01';
          
          /**
           * dom內(nèi)容加載完畢時,從API中加載數(shù)據(jù)
           */
          document.addEventListener("DOMContentLoaded", function(event) {
          fetch(api)
              .then(response => response.json())
              .then(data => {
                  let parsedData = parseData(data);
                  drawChart(parsedData);
              })
              .catch(err =>  console.log(err))
          });
          
          /**
           * 將數(shù)據(jù)解析為鍵值對
           */
          parseData = data =>{
              let arr = [];
              for (let i in data.bpi) {
                  arr.push({
                      date: new Date(i), //date
                      value: +data.bpi[i] //convert string to number
                  });
              }
              return arr;
          }
          
          /**
           * 創(chuàng)建圖表
           */
          drawChart  = data => {
          let svgWidth = 600, svgHeight = 400;
          let margin = { top: 20, right: 20, bottom: 30, left: 50 };
          let width = svgWidth - margin.left - margin.right;
          let height = svgHeight - margin.top - margin.bottom;
          
          let svg = d3.select('svg')
              .attr("width", svgWidth)
              .attr("height", svgHeight);
              
          let g = svg.append("g")
              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
          
          let x = d3.scaleTime()
              .rangeRound([0, width]);
          
          let y = d3.scaleLinear()
              .rangeRound([height, 0]);
          
          let line = d3.line()
              .x(d=> x(d.date))
              .y(d=> y(d.value))
              x.domain(d3.extent(data, function(d) { return d.date }));
              y.domain(d3.extent(data, function(d) { return d.value }));
          
          g.append("g")
              .attr("transform", "translate(0," + height + ")")
              .call(d3.axisBottom(x))
              .select(".domain")
              .remove();
          
          g.append("g")
              .call(d3.axisLeft(y))
              .append("text")
              .attr("fill", "#000")
              .attr("transform", "rotate(-90)")
              .attr("y", 6)
              .attr("dy", "0.71em")
              .attr("text-anchor", "end")
              .text("Price ($)");
          
          g.append("path")
              .datum(data)
              .attr("fill", "none")
              .attr("stroke", "steelblue")
              .attr("stroke-linejoin", "round")
              .attr("stroke-linecap", "round")
              .attr("stroke-width", 1.5)
              .attr("d", line);
          }
          
          復(fù)制代碼

          以上原實例均來自:Learn D3 for free。

          源碼地址:https://scrimba.com/g/gd3js

          scrimba是一個非常神奇的網(wǎng)站。它是使用交互式編碼截屏工具構(gòu)建的。


          所有的操作都是:


          暫停截屏視頻 → 編輯代碼 → 運(yùn)行它! → 查看更改

          非常值得安利一波。接下來進(jìn)入第二部分:Vue中使用D3.js的正確姿勢

          2. Vue中使用D3.js的正確姿勢

          我們將使用D3和Vue構(gòu)建一個基本的柱狀圖組件。網(wǎng)上有一堆例子,但我們將專注于寫Vue,而不是濫用D3。

          1. 安裝依賴

          首先,我們需要為項目安裝依賴項。我們可以簡單地安裝和使用D3整庫:

          npm i d3
          復(fù)制代碼

          但我在前面講到,實際上D3是幾個分庫的集合,考慮到項目的優(yōu)化,我們只安裝所需的模塊。



          使用Vue Cli 初始化項目即可。

          2. 創(chuàng)建柱狀圖



          3. 柱狀圖模塊導(dǎo)入



          4. 創(chuàng)建svg元素



          因Vue數(shù)據(jù)響應(yīng)的特性,我們不需要用到D3操作DOM的那套鏈?zhǔn)絼?chuàng)建。

          5. 數(shù)據(jù)與窗口大小響應(yīng)


          在mounted鉤子中,我們將為窗口調(diào)整大小事件添加一個監(jiān)聽器,它將觸發(fā)繪制動畫,并將<svg>大小設(shè)置為新窗口的比例。我們不會立即渲染,而是等待300毫秒,以確保完全調(diào)整窗口大小。


          以下是完整的BarChart.vue,請配合注釋食用:

          <template>
            <div id="container" class="svg-container" align="center">
              <h1>{{ title }}</h1>
              <svg v-if="redrawToggle === true" :width="svgWidth" :height="svgHeight">
                <g>
                  <rect
                    v-for="item in data"
                    class="bar-positive"
                    :key="item[xKey]"
                    :x="xScale(item[xKey])"
                    :y="yScale(0)"
                    :width="xScale.bandwidth()"
                    :height="0"
                  ></rect>
                </g>
              </svg>
            </div>
          </template>
          
          <script>
          import { scaleLinear, scaleBand } from "d3-scale";
          import { max, min } from "d3-array";
          import { selectAll } from "d3-selection";
          import { transition } from "d3-transition";
          
          export default {
            name: "BarChart",
            props: {
              title: String,
              xKey: String,
              yKey: String,
              data: Array
            },
            mounted() {
              this.svgWidth = document.getElementById("container").offsetWidth * 0.75;
              this.AddResizeListener();
              this.AnimateLoad();
            },
            data: () => ({
              svgWidth: 0,
              redrawToggle: true
            }),
            methods: {
              // 繪制柱形
              AnimateLoad() {
                selectAll("rect")
                  .data(this.data)
                  .transition()
                  .delay((d, i) => {
                    return i * 150;
                  })
                  .duration(1000)
                  .attr("y", d => {
                    return this.yScale(d[this.yKey]);
                  })
                  .attr("height", d => {
                    return this.svgHeight - this.yScale(d[this.yKey]);
                  });
              },
              // 調(diào)整窗口大小后300毫秒重新繪制圖表
              // 即響應(yīng)式繪制
              AddResizeListener() {
                window.addEventListener("resize", () => {
                  this.$data.redrawToggle = false;
                  setTimeout(() => {
                    this.$data.redrawToggle = true;
                    this.$data.svgWidth =
                      document.getElementById("container").offsetWidth * 0.75;
                    this.AnimateLoad();
                  }, 300);
                });
              }
            },
            computed: {
              dataMax() {
                return max(this.data, d => {
                  return d[this.yKey];
                });
              },
              dataMin() {
                return min(this.data, d => {
                  return d[this.yKey];
                });
              },
              xScale() {
                return scaleBand()
                  .rangeRound([0, this.svgWidth])
                  .padding(0.1)
                  .domain(
                    this.data.map(d => {
                      return d[this.xKey];
                    })
                  );
              },
              // 通過線性比例尺自動生成
              yScale() {
                return scaleLinear()
                  .rangeRound([this.svgHeight, 0])
                  .domain([this.dataMin > 0 ? 0 : this.dataMin, this.dataMax]);
              },
              svgHeight() {
                return this.svgWidth / 1.61803398875; // 黃金比例
              }
            }
          };
          </script>
          
          <style scoped>
          .bar-positive {
            fill: steelblue;
            transition: r 0.2s ease-in-out;
          }
          
          .bar-positive:hover {
            fill: brown;
          }
          
          .svg-container {
            display: inline-block;
            position: relative;
            width: 100%;
            padding-bottom: 1%;
            vertical-align: top;
            overflow: hidden;
          }
          </style>
          復(fù)制代碼

          我們將從父組件App.vue獲取數(shù)據(jù):

          <template>
            <div id="app">
              <BarChart title="Bar Chart" xKey="name" yKey="amount" :data="barChartData"/>
            </div>
          </template>
          
          <script>
          import BarChart from "./components/BarChart.vue";
          
          export default {
            name: "App",
            components: {
              BarChart
            },
            data: () => ({
              barChartData: [
                {
                  name: "張三",
                  amount: 25
                },
                {
                  name: "李四",
                  amount: 40
                },
                {
                  name: "老王",
                  amount: 15
                },
                {
                  name: "老賴",
                  amount: 9
                }
              ]
            })
          };
          </script>
          
          <style>
          #app {
            font-family: "Open Sans", Helvetica, Arial, sans-serif;
            -webkit-font-smoothing: antialiased;
            -moz-osx-font-smoothing: grayscale;
            text-align: center;
            color: #282f36;
            margin-top: 30px;
          }
          </style>
          復(fù)制代碼

          這時候yarn run serve后將會看到:



          好像還缺點顯示數(shù)值,考慮到該圖高度是根據(jù)比例尺生成,我們調(diào)整下y坐標(biāo):

          yScale() {
            return scaleLinear()
              .rangeRound([this.svgHeight, 0])
              .domain([this.dataMin > 0 ? 0 : this.dataMin + 2, this.dataMax + 2]);
          },
          復(fù)制代碼

          在AnimateLoad()末尾添加:

          selectAll("text")
            .data(this.data)
            .enter()
          復(fù)制代碼

          最后在<g>元素中添加:

          <text
            v-for="item in data"
            :key="item[xKey].amount"
            :x="xScale(item[xKey]) + 30"
            :y="yScale(item[yKey]) - 2"
            fill="red"
          >{{ item[xKey]}} {{ item[yKey]}}
          </text>
          復(fù)制代碼



          3. 參考文章



          • The Hitchhiker’s Guide to d3.js
          • D3 is not a Data Visualization Library
          • D3中常用的比例尺
          • D3 vs G2 vs Echarts
          • Dynamic Data Visualizations With Vue.js and D3

          4. 總結(jié)

          該庫幾乎憑 Mike Bostock 一人之力完成,且在學(xué)術(shù)界、專業(yè)團(tuán)隊中享有極大聲譽(yù)。



          • D3更接近底層,與 g2、echarts 不同,d3 能直接操作 svg,所以擁有極大的自由度,幾乎可以實現(xiàn)任何 2d 的設(shè)計需求。
          • 正如其名 Data Driven Documents,其本質(zhì)是將數(shù)據(jù)與 DOM 綁定,并將數(shù)據(jù)映射至 DOM 屬性上。
          • D3 長于可視化,而不止于可視化,還提供了數(shù)據(jù)處理、數(shù)據(jù)分析、DOM 操作等諸多功能。
          • 如果有想深耕數(shù)據(jù)可視化方面的前端,D3不得不學(xué)。



          掌握 D3 后,限制作品水平的只會是想象力而不再是技術(shù)。

          源碼地址:https://github.com/roger-hiro/d3-bar-chart-vuejs

          ?? 看完三件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:

          1. 點贊,讓更多的人也能看到這篇內(nèi)容(收藏不點贊,都是耍流氓 -_-)
          2. 關(guān)注公眾號「前端勸退師」,不定期分享原創(chuàng)知識。
          3. 原地址:https://juejin.im/post/5d1e074af265da1bca51f8ec

          animate.css是一堆很酷,有趣且跨瀏覽器的動畫,供你在項目中使用。非常適合強(qiáng)調(diào),主頁,滑塊和一般的加水效果。



          animate.css v4正在進(jìn)行許多改進(jìn)和重大更改,包括CSS自定義屬性支持(又稱CSS變量)和類前綴,以確保安全使用。感興趣的小伙伴可以上github關(guān)注進(jìn)展以及提供反饋!

          Github

          animate.css的受歡迎程度毋庸置疑,在Github上star數(shù)高達(dá)接近63k,這是一個非常可觀的數(shù)據(jù),我相信其實大多數(shù)人或多或少都用過它

          https://daneden.github.io/animate.css/



          安裝使用

          • 使用npm安裝
          $ npm install animate.css --save
          

          或者 yarn:

          $ yarn add animate.css
          

          要在你網(wǎng)站中使用animate.css,只需將樣式表放入文檔的<head>中,然后將動畫類(animated)與任何動畫名稱一起添加到元素中,那么一個簡單的動畫效果就實現(xiàn)了,一下就是一個最簡單的例子:

          <head>
           <link rel="stylesheet" href="animate.min.css">
          </head>
          

          <h1 class="animated infinite bounce delay-2s">Example</h1>
          

          以下是你可以使用的所用動畫效果class



          可以更改動畫的持續(xù)時間,添加延遲或更改動畫播放的次數(shù):

          .yourElement {
           animation-duration: 3s;
           animation-delay: 2s;
           animation-iteration-count: infinite;
          }
          


          • JavaScript的用法:

          將animate.css與Javascript結(jié)合使用時,可以使用animate.css進(jìn)行大量其他工作。一個簡單的例子:

          const element = document.querySelector('.my-element')
          element.classList.add('animated', 'bounceOutLeft')
          

          還可以檢測動畫何時結(jié)束:

          const element = document.querySelector('.my-element')
          element.classList.add('animated', 'bounceOutLeft')
          element.addEventListener('animationend', function() { doSomething() })
          

          可以使用以下簡單功能來添加和刪除動畫:

          function animateCSS(element, animationName, callback) {
           const node = document.querySelector(element)
           node.classList.add('animated', animationName)
           function handleAnimationEnd() {
           node.classList.remove('animated', animationName)
           node.removeEventListener('animationend', handleAnimationEnd)
           if (typeof callback === 'function') callback()
           }
           node.addEventListener('animationend', handleAnimationEnd)
          }
          

          并像這樣使用它:

          animateCSS('.my-element', 'bounce')
          
          // or
          animateCSS('.my-element', 'bounce', function() {
           // Do something after animation
          })
          

          注意,這些示例使用的是ES6的const聲明,不再支持IE10和某些古老的瀏覽器。



          • 設(shè)定延遲和速度:

          可以直接在元素的class屬性上添加延遲,如下所示:

          <div class="animated bounce delay-2s">Example</div>
          


          • 快慢class

          通過添加這些類,可以控制動畫的速度,如下所示:

          <div class="animated bounce faster">Example</div>
          


          • 自定義構(gòu)建

          Animate.css由gulp.js提供支持,這意味著你可以輕松創(chuàng)建自定義版本。

          總結(jié)

          有些時候你看到別人的網(wǎng)站,感覺速度也不是很快,但是很自然,那么很有可能是使用了動畫,使用動畫不會加快網(wǎng)站的訪問速度,但是可以讓網(wǎng)頁瀏覽器來更加的平滑、更加的自然,使用起來會感覺很舒適,不會給人卡頓的感覺!

          文首發(fā)自「慕課網(wǎng)」,想了解更多IT干貨內(nèi)容,程序員圈內(nèi)熱聞,歡迎關(guān)注!

          前端框架發(fā)展到今天,已經(jīng)出現(xiàn)了很多被廣泛認(rèn)同的理念,也可以叫作它們的特征。在所有的特征中,最具代表性的特征之一即是數(shù)據(jù)驅(qū)動。

          用戶交互的對象——界面

          不論 web 應(yīng)用的內(nèi)部邏輯如何組織,最終與用戶產(chǎn)生交互的仍然是界面(User Interface,簡稱 UI)。對 web 應(yīng)用來說,界面則主要由 DOM 元素來呈現(xiàn)。因此,歸根到底,為了讓用戶通過 web 應(yīng)用完成操作,DOM 元素需要根據(jù)實際需要來不斷變化。

          例如,用戶通過我們的頁面來查詢當(dāng)天蘋果的價格,頁面上有一個“查詢”按鈕,還有一個顯示蘋果價格的區(qū)域,此外,在查詢過程中,還有一個加載中的提示。

          <button>查詢</button>
          <div class="price">今日蘋果價格為 10.00/千克</div>
          <div class="loading">加載中</div>
          

          在查詢并顯示結(jié)果的過程中,DOM 實際上會經(jīng)歷這樣幾個狀態(tài):

          1. 鼠標(biāo)移動到按鈕上時,按鈕的樣式改變(通過 CSS:hover或者通過 JS)。
          2. 按鈕按下時,按鈕的樣式改變(通過 CSS:active或者通過 JS)。
          3. 清空價格區(qū)域的顯示內(nèi)容。
          4. 將加載中的提示顯示出來。
          5. 待查詢到數(shù)據(jù)后,將數(shù)據(jù)拼裝成文字“今日蘋果價格為 10.00/千克”,放入價格區(qū)域中。
          6. 將加載中的提示隱藏。

          命令式編程實現(xiàn)界面變化

          如果將 1 和 2 使用 CSS 來處理,那么早期我們的 JavaScript 代碼大概是這樣:

          // 3. 清空價格區(qū)域的顯示內(nèi)容
          price.innerHTML = '';
          // 4. 將加載中的提示顯示出來
          loading.style.display = 'block';
          // 查詢價格
          queryPrice(function(data){
              // 5. 待查詢到數(shù)據(jù)后,將數(shù)據(jù)拼裝成文字“今日蘋果價格為 10.00/千克”,放入價格區(qū)域中
              price.innerHTML = '今日蘋果價格為 ' + data.price + '/千克';
              // 6. 將加載中的提示隱藏
              loading.style.display = 'none';
          });
          

          即使是引入 jQuery 之類的庫,整個過程仍然不會有實質(zhì)性的變化,主要靠每一行 JavaScript 代碼命令式地修改 DOM 元素來達(dá)到修改界面的目的。

          數(shù)據(jù)驅(qū)動界面改動

          既然需要不斷修改界面,而命令式修改界面又如此繁瑣,是否有別的方法來修改界面呢?數(shù)據(jù)驅(qū)動的概念應(yīng)運(yùn)而生。它的基本思想是:使用數(shù)據(jù)來描述應(yīng)用的狀態(tài),將界面的修改與數(shù)據(jù)的修改綁定起來,從而實現(xiàn)數(shù)據(jù)的任何修改都直接反映到界面的修改。

          如果用一個公式來表示的話,可以寫成UI=F(state),UI 即指用戶界面,state指應(yīng)用的狀態(tài),而F則是應(yīng)用狀態(tài)與用戶界面的映射關(guān)系定義。它最直觀的理解是“應(yīng)用的任何狀態(tài),都可以通過一種確定的映射關(guān)系,反映到界面的某種狀態(tài)上”,因此只要狀態(tài)發(fā)生變化,界面也會發(fā)生變化。

          上述例子使用 Vue 來編寫:

          <template>
              <button @click="query">查詢</button>
              <div class="price">今日蘋果價格為 {{price}}/千克</div>
              <div class="loading" v-show="isLoading">加載中</div>
          </template>
          <script>
              export default {
                  data(){
                      return {
                          price: 0,
                          isLoading: false
                      }
                  },
                  methods: {
                      query(){
                          this.isLoading = true;
                          queryPrice((data) => {
                              this.price = data.price;
                              this.isLoading = false;
                          });
                      }
                  }
              }
          </script>
          

          在上述代碼中,應(yīng)用的狀態(tài)就是data,包括兩個值priceisLoading。我們的邏輯代碼只需要對這兩個值進(jìn)行操作,即可以完成整個應(yīng)用功能的界面變化。

          而狀態(tài)和界面的映射則由<template>來定義,例如今日蘋果價格為 {{price}}/千克表示狀態(tài)中的price變量需要顯示在此處,v-show="isLoading"則表示是否顯示加載中完全由變量值isloading決定。

          事實上當(dāng)前前端主流的幾大框架,在界面的渲染上都不約而同選擇了數(shù)據(jù)驅(qū)動的方式,深入理解這一模式有助于我們更好地理解前端框架。

          數(shù)據(jù)驅(qū)動帶來的好處

          數(shù)據(jù)驅(qū)動界面變更能迅速被廣泛接受,也可以從側(cè)面說明它確實為開發(fā)者帶來了一些好處。

          首先,因為開發(fā)者僅需要管理數(shù)據(jù),使得關(guān)于界面細(xì)節(jié)控制的代碼不再需要開發(fā)者編寫。同時,由于狀態(tài)被抽象出來,同一個變量值在界面上的多處變化全部由映射關(guān)系來決定,而不需要開發(fā)者手工修改每一處變化。這兩者結(jié)合起來使得開發(fā)者的心智負(fù)擔(dān)大大減少,需要關(guān)注的代碼量也大大減少,從而使得開發(fā)效率得以大幅提升,出現(xiàn) bug 的概率也大大減少。

          其次,專門將應(yīng)用狀態(tài)抽象出來,使得開發(fā)者必須認(rèn)真思考代碼的組織方式,而因為界面相關(guān)細(xì)節(jié)的消失,大部分的代碼都變成了邏輯代碼,使得傳統(tǒng)編程中的模式都可以被應(yīng)用到前端代碼中,從而使得前端代碼能夠支持更大規(guī)模的應(yīng)用,也能更好地組織前端代碼本身,使得代碼更容易閱讀和維護(hù)。

          狀態(tài)的抽象也使得開發(fā)者可以精準(zhǔn)地保存和還原任意一個界面狀態(tài)。因為界面的每一時刻的界面表現(xiàn)都是由這一時刻的應(yīng)用狀態(tài)決定,因此只要能夠?qū)⒋藭r的應(yīng)用狀態(tài)進(jìn)行保存,就能在另一個時間、空間中重現(xiàn)應(yīng)用此時的界面表現(xiàn)。

          這個特性在某一些場景下非常好用,例如線上 bug 的排查。如果我們有辦法取到用戶的當(dāng)前狀態(tài),就有辦法完全還原用戶的界面表現(xiàn),從而快速復(fù)現(xiàn)應(yīng)用碰到的 bug,而不用再苦苦和用戶溝通詳細(xì)的操作步驟,一點點地確認(rèn)應(yīng)用可能是哪里出了問題。除此之外,這個特性還可以用于實現(xiàn)“時間旅行”效果,即應(yīng)用界面的回放。我們只需要將狀態(tài)的變更都記錄下來,就能看到應(yīng)用從初始化一直到最終狀態(tài)中間發(fā)生的完整事情。它本身可以作為一個效果來使用,也可以用來支持一些功能(例如撤銷/重做)。

          因為應(yīng)用界面完全由應(yīng)用狀態(tài)決定,而狀態(tài)映射到界面的操作一般由框架來幫忙我們完成,因此在測試的時候,就有機(jī)會將重點放在狀態(tài)的測試上。即在很多情況下,我們只需要測試邏輯和數(shù)據(jù),確保應(yīng)用狀態(tài)是正確的,即可大概認(rèn)為界面是正確的。

          因為界面測試的成本要遠(yuǎn)高于邏輯和數(shù)據(jù)測試,如果我們能在不做界面測試的情況下也保證應(yīng)用邏輯和狀態(tài)是正確的,將大大提升測試效率。

          小結(jié)

          時至今日,數(shù)據(jù)驅(qū)動已經(jīng)成為絕大部分前端開發(fā)者的共識,Vue 也是這一理論的實踐者,后面我們將看到 Vue 是如何實現(xiàn)這一重要特征的。

          歡迎關(guān)注「慕課網(wǎng)」,發(fā)現(xiàn)更多IT圈優(yōu)質(zhì)內(nèi)容,分享干貨知識,幫助你成為更好的程序員!


          主站蜘蛛池模板: 午夜福利av无码一区二区| 夜夜嗨AV一区二区三区| 国产精品男男视频一区二区三区| 色窝窝无码一区二区三区色欲| 色一乱一伦一图一区二区精品 | 国产美女一区二区三区| 久久亚洲色一区二区三区| 日韩内射美女人妻一区二区三区| 夜夜精品无码一区二区三区| 国产一区二区草草影院| 激情一区二区三区| 国产精久久一区二区三区| 精品91一区二区三区| 国产一区二区三区四| 无码视频免费一区二三区| 亚洲AV无码国产精品永久一区| 人妻激情偷乱视频一区二区三区| 精品视频一区二区三区在线观看 | 久久综合精品不卡一区二区| 一区二区三区四区国产| 人妻无码一区二区视频| 无码欧精品亚洲日韩一区夜夜嗨 | 日本强伦姧人妻一区二区| 无码视频免费一区二三区| 无码一区二区三区在线| 日本一道高清一区二区三区| 午夜福利无码一区二区| 国产成人综合亚洲一区| 日本一区二区三区精品国产| 果冻传媒一区二区天美传媒| 久久精品无码一区二区三区| 亚洲av无码一区二区三区乱子伦 | 秋霞午夜一区二区| 国产福利精品一区二区| 久久久国产精品亚洲一区| 中文乱码人妻系列一区二区| 精品免费国产一区二区| 日韩精品一区二区三区不卡| 三上悠亚一区二区观看| 3d动漫精品啪啪一区二区中文| 无码乱码av天堂一区二区|