DEV Community

ZHZL-m
ZHZL-m

Posted on

【Journey of HarmonyOS Next】Developing Based on ArkTS (3) -> JS-Compatible Web Development (7) -> JS Animation (3)

Image description

1 -> Animated animations

1.1 -> Create an animated object

Create an animated object by creating an animator and set the properties of the animation by setting the parameter options.

<!-- test.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>
Enter fullscreen mode Exit fullscreen mode
/* test.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;
}
Enter fullscreen mode Exit fullscreen mode
/* test.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();
  }
}
Enter fullscreen mode Exit fullscreen mode

illustrate

When using createAnimator to create an animated object, you must pass in the options parameter.

The starting point of the BEGIN interpolation is 0 by default.

end is interpolated with the end point, which is 1 by default when not set.

1.2 -> Add animation events and call APIs

Animator supports events and APIs, and you can customize animation effects by adding frame, cancel, repeat, and finish events and calling update, play, pause, cancel, reverse, and finish APIs. For more information about the events and APIs supported by animator, see createAnimator in Animation.

<!-- test.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>

/* test.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;
}
Enter fullscreen mode Exit fullscreen mode
/* test.js */
import animator from '@ohos.animator';
import prompt from '@system.prompt';
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() {
      prompt.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() {
      prompt.showToast({
        message: 'finish'
      })
    };
    this.animation.finish(); //调用动画完成接口
  },
  cancelAnimation() {
    this.animation.cancel(); //调用动画取消接口
  },
  reverseAnimation() {
    this.animation.reverse(); //调用动画倒放接口
  }
}
Enter fullscreen mode Exit fullscreen mode

illustrate

You can use this interface to update animation parameters during the call to createAnimator.

2 -> animation frames

2.1 -> Request animation frames

When an animation frame is requested, the requestAnimationFrame function is called back frame by frame, and a callback function is passed when the function is called.

When the runframe calls requestAnimationFrame, it passes in the callback function step with the timestamp parameter, and assigns the timestamp in the step to the starting startTime. When the difference between timestamp and startTime is less than the specified time, requestAnimationFrame will be called again, and the animation will eventually stop.

<!-- test.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>
Enter fullscreen mode Exit fullscreen mode
/* test.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}
button{
  width: 300px;
}
Enter fullscreen mode Exit fullscreen mode
/* test.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);
    }
}

Enter fullscreen mode Exit fullscreen mode

illustrate

When the requestAnimationFrame function calls the callback function, the timestamp is passed in the first parameter, indicating the time when the requestAnimationFrame starts to execute the callback function.

2.2 -> Cancels the animation frame

You can use the cancelAnimationFrame function to cancel the frame-by-frame callback and cancel the request request of the requestAnimationFrame function when the cancelAnimationFrame function is called.

<!-- test.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>
Enter fullscreen mode Exit fullscreen mode
/* test.css */
.container {
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
}
button{
  width: 300px;
}
Enter fullscreen mode Exit fullscreen mode
/* test.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);
  }
}
Enter fullscreen mode Exit fullscreen mode

illustrate

When calling this function, you need to pass in a parameter with an identification id.

3 -> Custom components

The Ark Development Framework, which uses a JS-compatible Web development paradigm, supports custom components, which can be extended by users according to business needs, adding custom private attributes and events, and encapsulating them into new components, which are convenient to call multiple times in the project and improve the readability of the page layout code. The following is an example of how to encapsulate the application:

Build custom components

<!-- comp.hml -->
 <div class="item"> 
   <text class="title-style">{{title}}</text>
   <text class="text-style" onclick="childClicked" focusable="true">点击这里查看隐藏文本</text>
   <text class="text-style" if="{{showObj}}">hello world</text>
 </div>
Enter fullscreen mode Exit fullscreen mode
/* comp.css */
 .item { 
   width: 700px;  
   flex-direction: column;  
   height: 300px;  
   align-items: center;  
   margin-top: 100px; 
 }
 .text-style {
   width: 100%;
   text-align: center;
   font-weight: 500;
   font-family: Courier;
   font-size: 36px;
 }
 .title-style {
   font-weight: 500;
   font-family: Courier;
   font-size: 50px;
   color: #483d8b;
 }

Enter fullscreen mode Exit fullscreen mode
// comp.js
 export default {
   props: {
     title: {
       default: 'title',
     },
     showObject: {},
   },
   data() { 
     return {
       showObj: this.showObject,
     };
   }, 
   childClicked () { 
     this.$emit('eventType1', {text: '收到子组件参数'});
     this.showObj = !this.showObj; 
   }, 
 }
Enter fullscreen mode Exit fullscreen mode

Introduce custom components

<!-- test.hml -->
 <element name='comp' src='../../common/component/comp.hml'></element> 
 <div class="container"> 
   <text>父组件:{{text}}</text>
   <comp title="自定义组件" show-object="{{isShow}}" @event-type1="textClicked"></comp>
 </div>
Enter fullscreen mode Exit fullscreen mode
/* test.css */
 .container { 
   background-color: #f8f8ff; 
   flex: 1; 
   flex-direction: column; 
   align-content: center;
 } 
Enter fullscreen mode Exit fullscreen mode
// test.js
 export default { 
   data: {
     text: '开始',
     isShow: false,
   },
   textClicked (e) {
     this.text = e.detail.text;
   },
 }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)