Vue过渡状态

2017-09-13 20:32:46来源:cnblogs.com作者:小火柴的蓝色理想人点击

分享

前面的话

  Vue 的过渡系统提供了非常多简单的方法设置进入、离开和列表的动效。那么对于数据元素本身的动效呢?包括数字和运算、颜色的显示、SVG 节点的位置、元素的大小和其他的属性等。所有的原始数字都被事先存储起来,可以直接转换到数字。做到这一步,我们就可以结合 Vue 的响应式和组件系统,使用第三方库来实现切换元素的过渡状态

状态动画

  通过watcher,能监听到任何数值属性的数值更新

<div id="animated-number-demo">  <input v-model.number="number" type="number" step="20">  <p>{{ animatedNumber }}</p></div><script src="Tween.js"></script><script src="vue.js"></script> <script>new Vue({  el: '#animated-number-demo',  data: {    number: 0,    animatedNumber: 0  },  watch: {    number: function(newValue, oldValue) {      var vm = this;      function animate () {        if (TWEEN.update()) {          requestAnimationFrame(animate)        }      }      new TWEEN.Tween({ tweeningNumber: oldValue })        .easing(TWEEN.Easing.Quadratic.Out)        .to({ tweeningNumber: newValue }, 500)        .onUpdate(function () {          vm.animatedNumber = this.tweeningNumber.toFixed(0)        })        .start();      animate()    }  }})    </script>  

  当把数值更新时,就会触发动画。这个是一个不错的演示,但是对于不能直接像数字一样存储的值,比如 CSS 中的 color 的值,通过下面的例子来通过 Color.js 实现一个例子:

<div id="example">  <input v-model="colorQuery" @keyup.enter="updateColor" placeholder="Enter a color">  <button @click="updateColor">Update</button>  <p>Preview:</p>  <span :style="{ backgroundColor: tweenedCSSColor }" style="display: inline-block;width: 50px;height: 50px;"></span>  <p>{{ tweenedCSSColor }}</p></div><script src="Tween.js"></script><script src="vue.js"></script> <script src="color.js"></script><script>var Color = net.brehaut.Colornew Vue({  el: '#example',  data: {    colorQuery: '',    color: {      red: 0,      green: 0,      blue: 0,      alpha: 1    },    tweenedColor: {}  },  created: function () {    this.tweenedColor = Object.assign({}, this.color)  },  watch: {    color: function () {      function animate () {        if (TWEEN.update()) {          requestAnimationFrame(animate)        }      }      new TWEEN.Tween(this.tweenedColor)        .to(this.color, 750)        .start()      animate()    }  },  computed: {    tweenedCSSColor: function () {      return new Color({        red: this.tweenedColor.red,        green: this.tweenedColor.green,        blue: this.tweenedColor.blue,        alpha: this.tweenedColor.alpha      }).toCSS()    }  },  methods: {    updateColor: function () {      this.color = new Color(this.colorQuery).toRGB()      this.colorQuery = ''    }  }})</script>  

动态状态转换

  就像 Vue 的过渡组件一样,数据背后状态转换会实时更新,这对于原型设计十分有用。当修改一些变量,即使是一个简单的 SVG 多边形也可以实现很多难以想象的效果

<style>svg,input[type="range"]{display:block;}    </style><div id="app">  <svg width="200" height="200">    <polygon :points="points" fill="#41B883"></polygon>    <circle cx="100" cy="100" r="90" fill=" transparent" stroke="#35495E"></circle>  </svg>  <label>Sides: {{ sides }}</label>  <input  type="range" min="3"  max="500" v-model.number="sides">  <label>Minimum Radius: {{ minRadius }}%</label>  <input  type="range"  min="0"  max="90"  v-model.number="minRadius">  <label>Update Interval: {{ updateInterval }} milliseconds</label>  <input  type="range"  min="10"  max="2000" v-model.number="updateInterval"></div>    <script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/vue.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.5/TweenLite.min.js"></script><script>new Vue({  el: '#app',  data: function () {    //默认有10条边    var defaultSides = 10;    //默认地,stats = [100, 100, 100, 100, 100, 100, 100, 100, 100, 100]    var stats = Array.apply(null, { length: defaultSides })        .map(function () { return 100 })    return {        stats: stats,        points: generatePoints(stats),        sides: defaultSides,        minRadius: 50,        interval: null,        updateInterval: 500    }  },  watch: {    sides: function (newSides, oldSides) {        //计算设置的边数与默认的边数的差值        var sidesDifference = newSides - oldSides        //如果大于默认边数        if (sidesDifference > 0) {            //增加相应数量的随机值到stats数组中            for (var i = 1; i <= sidesDifference; i++) {                this.stats.push(this.newRandomValue())            }        }else{            //否则,计算出差值            var absoluteSidesDifference = Math.abs(sidesDifference)            //从stats数组末尾减少相应数量的数组值            for (var i = 1; i <= absoluteSidesDifference; i++) {                this.stats.shift()            }        }    },    stats: function (newStats) {        TweenLite.to(            this.$data,             this.updateInterval / 1000,             { points: generatePoints(newStats) }        )    },    updateInterval: function () {        this.resetInterval()    }  },  mounted: function () {    this.resetInterval()  },  methods: {    //将stats里面的值都变成50-100的随机值    randomizeStats: function () {        var vm = this        this.stats = this.stats.map(function () {        return vm.newRandomValue()      })    },    newRandomValue: function () {        //产生一个50-100的随机半径        return Math.ceil(this.minRadius + Math.random() * (100 - this.minRadius))    },    //重启定时器    resetInterval: function () {        var vm = this;        clearInterval(this.interval);        this.randomizeStats();        this.interval = setInterval(function () {          vm.randomizeStats();        }, this.updateInterval)    }  }})function valueToPoint (value, index, total) {  var x     = 0  var y     = -value * 0.9  var angle = Math.PI * 2 / total * index  var cos   = Math.cos(angle)  var sin   = Math.sin(angle)  var tx    = x * cos - y * sin + 100  var ty    = x * sin + y * cos + 100  return { x: tx, y: ty }}//计算polygon中的路径点的值function generatePoints (stats) {    var total = stats.length    return stats.map(function (stat, index) {        var point = valueToPoint(stat, index, total)        return point.x + ',' + point.y  }).join(' ')}</script>  

组件组织过渡

  管理太多的状态转换会很快的增加 Vue 实例或者组件的复杂性,幸好很多的动画可以提取到专用的子组件

<div id="example">  <input v-model.number="firstNumber" type="number" step="20"> +  <input v-model.number="secondNumber" type="number" step="20"> =  {{ result }}  <p>    <animated-integer :value="firstNumber"></animated-integer> +    <animated-integer :value="secondNumber"></animated-integer> =    <animated-integer :value="result"></animated-integer>  </p></div><script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/vue.js"></script><script type="text/javascript" src="http://sandbox.runjs.cn/uploads/rs/26/ddzmgynp/Tween.js"></script><script>Vue.component('animated-integer', {  template: '<span>{{ tweeningValue }}</span>',  props: {    value: {      type: Number,      required: true    }  },  data: function () {    return {      tweeningValue: 0    }  },  watch: {    value: function (newValue, oldValue) {      this.tween(oldValue, newValue)    }  },  mounted: function () {    this.tween(0, this.value)  },  methods: {    tween: function (startValue, endValue) {      var vm = this;      function animate () {        if (TWEEN.update()) {          requestAnimationFrame(animate)        }      }      new TWEEN.Tween({ tweeningValue: startValue })        .to({ tweeningValue: endValue }, 500)        .onUpdate(function () {          vm.tweeningValue = this.tweeningValue.toFixed(0)        })        .start()      animate()    }  }})new Vue({  el: '#example',  data: {    firstNumber: 20,    secondNumber: 40  },  computed: {    result: function () {      return this.firstNumber + this.secondNumber    }  }})</script>  

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台