乐趣区

关于harmonyos:开发指导利用组件插值器动画实现HarmonyOS动效

一. 组件动画
在组件上创立和运行动画的快捷方式。具体用法请参考通用办法。

获取动画对象
通过调用 animate 办法取得 animation 对象,animation 对象反对动画属性、动画办法和动画事件。

<!-- xxx.hml -->
<div class="container">
  <div id="content" class="box" onclick="Show"></div>
</div>
/* xxx.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
}
.box{
  width: 200px;
  height: 200px;
  background-color: #ff0000;
  margin-top: 30px;
}
/* xxx.js */
export default {
  data: {animation: '',},
  onInit() {},
  onShow() {
    var options = {duration: 1500,};
    var frames = [
      {width:200,height:200,},
      {width:300,height:300,}
    ];
    this.animation = this.$element('content').animate(frames, options);  // 获取动画对象
  },
  Show() {this.animation.play();
  }
}

阐明
● 应用 animate 办法时必须传入 Keyframes 和 Options 参数。
● 屡次调用 animate 办法时,采纳 replace 策略,即最初一次调用时传入的参数失效。

设置动画参数
在获取动画对象后,通过设置参数 Keyframes 设置动画在组件上的款式。

<!-- xxx.hml -->
<div class="container">
   <div id="content" class="box" onclick="Show"></div>
</div>
/* xxx.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}
.box{
  width: 200px;
  height: 200px;
  background-color: #ff0000;
  margin-top: 30px;
}
/* xxx.js */
export default {
  data: {
    animation: '',
    keyframes:{},
    options:{}},
  onInit() {
    this.options = {duration: 4000,}
    this.keyframes = [
    {
      transform: {
        translate: '-120px -0px',   
        scale: 1,        
        rotate: 0
        },   
        transformOrigin: '100px 100px',  
        offset: 0.0, 
        width: 200,  
        height: 200   
      }, 
      {
        transform: {      
          translate: '120px 0px',     
          scale: 1.5,     
          rotate: 90   
          },
          transformOrigin: '100px 100px',
          offset: 1.0,
          width: 300,
          height: 300   
      }    
    ]
  },
  Show() {this.animation = this.$element('content').animate(this.keyframes, this.options)
    this.animation.play()}
}

阐明
● translate、scale 和 rtotate 的先后顺序会影响动画成果。
● transformOrigin 只对 scale 和 rtotate 起作用。

在获取动画对象后,通过设置参数 Options 来设置动画的属性。

<!-- xxx.hml -->
<div class="container">
   <div id="content" class="box" onclick="Show"></div>
</div>
/* xxx.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
}
.box{
  width: 200px;
  height: 200px;
  background-color: #ff0000;
  margin-top: 30px;
}
/* xxx.js */
export default {
  data: {animation: '',},
  onInit() {},
  onShow() {
    var options = {      
        duration: 1500,      
        easing: 'ease-in',      
        delay: 5,      
        iterations: 2,      
        direction: 'normal',    
    };
    var frames = [
      {
        transform: {translate: '-150px -0px'}
      },
      {
        transform: {translate: '150px 0px'}
      }
    ];
    this.animation = this.$element('content').animate(frames, options);
  },
  Show() {this.animation.play();
  }
}

阐明
direction:指定动画的播放模式。
normal:动画正向循环播放。
reverse:动画反向循环播放。
alternate:动画交替循环播放,奇数次正向播放,偶数次反向播放。
alternate-reverse:动画反向交替循环播放,奇数次反向播放,偶数次正向播放。

二.  插值器动画

动画动效
通过设置插值器来实现动画成果。(从 API Version 6 开始反对。)

创立动画对象
通过 createAnimator 创立一个动画对象,通过设置参数 options 来设置动画的属性。

<!-- xxx.hml -->
<div class="container">
  <div style="width: 300px;height: 300px;margin-top: 100px;background: linear-gradient(pink, purple);transform: translate({{translateVal}});">
  </div>
  <div class="row">
    <button type="capsule" value="play" onclick="playAnimation"></button>
  </div>
</div>
/* xxx.css */
.container {
  width:100%;
  height:100%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
button{width: 200px;}
.row{
  width: 65%;
  height: 100px;
  align-items: center;
  justify-content: space-between;
  margin-top: 50px;
  margin-left: 260px;
}
// xxx.js
import animator from '@ohos.animator';
export default {
  data: {
    translateVal: 0,
    animation: null
  },
  onInit() {},
  onShow(){
    var options = {
      duration: 3000,
      easing:"friction",
      delay:"1000",
      fill: 'forwards',
      direction:'alternate',
      iterations: 2,
      begin: 0,
      end: 180
    };// 设置参数
    this.animation = animator.createAnimator(options)// 创立动画
  },
  playAnimation() {
    var _this = this;
    this.animation.onframe = function(value) {_this.translateVal= value};
    this.animation.play();}
}

阐明
● 应用 createAnimator 创立动画对象时必须传入 options 参数。
● begin 插值终点,不设置时默认为 0。
● end 插值起点,不设置时默认为 1。

增加动画事件和调用接口
animator 反对事件和接口,能够通过增加 frame、cancel、repeat、finish 事件和调用 update、play、pause、cancel、reverse、finish 接口自定义动画成果。animator 反对的事件和接口具体见动画中的 createAnimator。

<!-- xxx.hml -->
<div style="flex-direction: column;align-items: center;width: 100%;height: 100%;">
  <div style="width:200px;height: 200px;margin-top: 100px;background: linear-gradient(#b30d29, #dcac1b);
  transform: scale({{scaleVal}});"></div>
  <div style="width: {{DivWidth}};height: {{DivHeight}};margin-top: 200px;
  background: linear-gradient(pink, purple);margin-top: 200px;transform:translateY({{translateVal}});">
  </div>
  <div class="row">
    <button type="capsule" value="play" onclick="playAnimation"></button>
    <button type="capsule" value="update" onclick="updateAnimation"></button>
  </div>
  <div class="row1">
    <button type="capsule" value="pause" onclick="pauseAnimation"></button>
    <button type="capsule" value="finish" onclick="finishAnimation"></button>
  </div>
  <div class="row2">
    <button type="capsule" value="cancel" onclick="cancelAnimation"></button>
    <button type="capsule" value="reverse" onclick="reverseAnimation"></button>
  </div>
</div>
/* xxx.css */
button{width: 200px;}
.row{
  width: 65%;
  height: 100px;
  align-items: center;
  justify-content: space-between;
  margin-top: 150px;
  position: fixed;
  top: 52%;
  left: 120px;
}
.row1{
  width: 65%;
  height: 100px;
  align-items: center;
  justify-content: space-between;
  margin-top: 120px;
  position: fixed;
  top: 65%;
  left: 120px;
}
.row2{
  width: 65%;
  height: 100px;
  align-items: center;
  justify-content: space-between;
  margin-top: 100px;
  position: fixed;
  top: 75%;
  left: 120px;
}
// xxx.js
import animator from '@ohos.animator';
import promptAction from '@ohos.promptAction';
export default {
  data: {
    scaleVal:1,
    DivWidth:200,
    DivHeight:200,
    translateVal:0,
    animation: null
  },
  onInit() {
    var options = {
      duration: 3000,
      fill: 'forwards',
      begin: 200,
      end: 270
    };
    this.animation = animator.createAnimator(options);
  },
  onShow() {
    var _this= this;
    // 增加动画重放事件
    this.animation.onrepeat = function() {
      promptAction.showToast({message: 'repeat'});
      var repeatoptions = {
        duration: 2000,
        iterations: 1,
         direction: 'alternate',
         begin: 180,
         end: 240
       };
        _this.animation.update(repeatoptions);
        _this.animation.play();};
  },
  playAnimation() {
    var _this= this;
    // 增加动画逐帧插值回调事件
    this.animation.onframe = function(value) {
      _this. scaleVal= value/150,
      _this.DivWidth = value,
      _this.DivHeight = value,
      _this.translateVal = value-180
    };
    this.animation.play();},
  updateAnimation() {
    var newoptions = {
      duration: 5000,
      iterations: 2,
      begin: 120,
      end: 180
    };
    this.animation.update(newoptions);
    this.animation.play();// 调用动画播放接口},
  pauseAnimation() {this.animation.pause();// 调用动画暂停接口
  },
  finishAnimation() {
    var _this= this;
   // 增加动画实现事件
    this.animation.onfinish = function() {
      promptAction.showToast({message: 'finish'})
    };
    this.animation.finish(); // 调用动画实现接口},
  cancelAnimation() {this.animation.cancel(); // 调用动画勾销接口
  },
  reverseAnimation() {this.animation.reverse(); // 调用动画倒放接口
  }
}

阐明
在调用 update 接口的过程中能够应用这个接口更新动画参数,入参加 createAnimator 统一。

动画帧

申请动画帧
申请动画帧时通过 requestAnimationFrame 函数逐帧回调,在调用该函数时传入一个回调函数。

runframe 在调用 requestAnimationFrame 时传入带有 timestamp 参数的回调函数 step,将 step 中的 timestamp 赋予起始的 startTime。当 timestamp 与 startTime 的差值小于规定的工夫时将再次调用 requestAnimationFrame,最终动画将会进行。

<!-- xxx.hml -->
<div class="container">
  <tabs onchange="changecontent">
    <tab-content>
      <div class="container">
        <stack style="width: 300px;height: 300px;margin-top: 100px;margin-bottom: 100px;">
          <canvas id="mycanvas" style="width: 100%;height: 100%;background-color: coral;">
          </canvas>
          <div style="width: 50px;height: 50px;border-radius: 25px;background-color: indigo;position: absolute;left: {{left}};top: {{top}};">
          </div>
        </stack>
        <button type="capsule" value="play" onclick="runframe"></button>
      </div>
    </tab-content>
  </tabs>
</div>
/* xxx.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}
button{width: 300px;}
// xxx.js
export default {
  data: {
    timer: null,
    left: 0,
    top: 0,
    flag: true,
    animation: null,
    startTime: 0,
  },
  onShow() {var test = this.$element("mycanvas");
    var ctx = test.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(300, 300);
    ctx.lineWidth = 5;
    ctx.strokeStyle = "red";
    ctx.stroke();},
  runframe() {
    this.left = 0;
    this.top = 0;
    this.flag = true;
    this.animation = requestAnimationFrame(this.step);
  },
  step(timestamp) {if (this.flag) {
      this.left += 5;
      this.top += 5;
      if (this.startTime == 0) {this.startTime = timestamp;}
      var elapsed = timestamp - this.startTime;
        if (elapsed < 500) {console.log('callback step timestamp:' + timestamp);
          this.animation = requestAnimationFrame(this.step);
        }
      } else {
        this.left -= 5;
        this.top -= 5;
        this.animation = requestAnimationFrame(this.step);
      }
      if (this.left == 250 || this.left == 0) {this.flag = !this.flag}
    },
    onDestroy() {cancelAnimationFrame(this.animation);
    }
}

阐明
requestAnimationFrame 函数在调用回调函数时在第一个参数地位传入 timestamp 工夫戳,示意 requestAnimationFrame 开始去执行回调函数的时刻。

勾销动画帧
通过 cancelAnimationFrame 函数勾销逐帧回调,在调用 cancelAnimationFrame 函数时勾销 requestAnimationFrame 函数的申请。

<!-- xxx.hml -->
<div class="container">
  <tabs onchange="changecontent">
    <tab-content>
      <div class="container">
        <stack style="width: 300px;height: 300px;margin-top: 100px;margin-bottom: 100px;">
          <canvas id="mycanvas" style="width: 100%;height: 100%;background-color: coral;">
          </canvas>
          <div style="width: 50px;height: 50px;border-radius: 25px;background-color: indigo;position: absolute;left: {{left}};top: {{top}};">
          </div>
        </stack>
        <button type="capsule" value="play" onclick="runframe"></button>
      </div>
    </tab-content>
  </tabs>
</div>
/* xxx.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}
button{width: 300px;}
// xxx.js
export default {
  data: {
    timer: null,
    left: 0,
    top: 0,
    flag: true,
    animation: null
  },
  onShow() {var test = this.$element("mycanvas");
    var ctx = test.getContext("2d");
    ctx.beginPath();
    ctx.moveTo(0, 0);
    ctx.lineTo(300, 300);
    ctx.lineWidth = 5;
    ctx.strokeStyle = "red";
    ctx.stroke();},
  runframe() {
    this.left = 0;
    this.top = 0;
    this.flag = true;
    this.animation = requestAnimationFrame(this.step);
  },
  step(timestamp) {if (this.flag) {
      this.left += 5;
      this.top += 5;
      this.animation = requestAnimationFrame(this.step);
    } else {
      this.left -= 5;
      this.top -= 5;
      this.animation = requestAnimationFrame(this.step);
    }
    if (this.left == 250 || this.left == 0) {this.flag = !this.flag}
  },
  onDestroy() {cancelAnimationFrame(this.animation);
  }
}

阐明
在调用该函数时需传入一个具备标识 id 的参数。

退出移动版