การใช้งาน Animation สำหรับกำหนดการเคลื่อนไหว ใน Angular

เขียนเมื่อ 6 ปีก่อน โดย Ninenik Narkdee
animation state angular transition

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ animation state angular transition

ดูแล้ว 8,467 ครั้ง


เนื้อหาของตอนต่อไปนี้ จะเป็นแนวทางการใช้งาน Animation สำหรับกำหนดการเคลื่อนไหว
ในรูปแบบต่างๆ ให้กับการเปลี่ยนแปลงการแสดงผล เช่น การเปลี่ยนหน้าเพจ การแสดงรายการข้อมูล
ด้วยการเคลื่อนไหว ซึ่งนอกจากจะทำให้ app ของเราดูน่าสนใจแล้ว อาจจะยังทำให้การใช้งาน เป็นไป
อย่างง่ายสะดวกและง่ายขึ้นด้วยก็ได้
    การใช้งาน animation ใน angular นั้นจะใช้การทำงานในลักษณะเดียวกับการกำหนด animation ด้วย
css ซึ่งทำให้การจัดการกับ animation นั้นเป็นไปอยางง่ายดาย

การตั้งค่าก่อนใช้งาน Animation

    ก่อนที่เราจะมีการใช้งาน animation ใน app เราจำเป็นต้องทำการ import module ที่เกี่ยวข้องเข้ามาใช้งาน
ใน AppModule ของเราก่อน ดังนี้ ดูบรรทัดส่วนที่ highlight 
 
ไฟล์ app.module.ts 
 
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HttpClientModule } from '@angular/common/http';
import { FormsModule }   from '@angular/forms';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

import { AppRoutingModule } from './app-routing.module';
import { ArticleModule } from './article/article.module';
import { RegisterModule } from './register/register.module';

import { AppComponent } from './app.component';
import { PagenofoundComponent } from './pagenofound/pagenofound.component';
import { HomeComponent } from './home/home.component';
import { HighlightDirective } from './home/highlight.directive';

@NgModule({
  declarations: [
    AppComponent,
    PagenofoundComponent,
    HomeComponent,
    HighlightDirective
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    FormsModule,
    HttpClientModule,
    ArticleModule,
    RegisterModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
 
 
หลังจากที่เราทำการ import ในบรรทัดที่ 6 และ 26 ลงใน AppModule แล้ว เราก็สามารถเรียกใช้งาน
การกำหนด animation ใน Angular app ได้แล้ว ต่อไปเราจะมาดูการใช้งาน และการกำหนดเพื่อให้
animation ที่เราต้องการทำงาน

 

การเปลี่ยนรูปแบบระหว่าง state

    การทำ animation หรือการกำหนดการเคลื่อนไหว หลักๆ ก็คือการเปลี่ยนสภาพ หรือการเปลี่ยนรูปแบบ 
หรือการเปลี่ยนรูปร่าง จากสภาวะหนึ่งไปอีกสภาวะหนึ่ง ซึ่งใน angular จะใช้คำว่า state โดย state นั้นจะมีค่าเป็น 
string เพื่อระบุสภาวะที่เราต้องการ เช่น 'active' หรือ 'inactive'     ในแต่ละ state เราสามารถกำหนด style ให้กับ
state นั้นๆ รูปแบบการกำหนด style จะใช้รูปแบบเดียวกับ css แต่การเขียนอาจจะแตกต่างกันในชื่อ property
ยกตัวอยาง background-color ก็จะใช้เป็น backgroundColor เป็นต้น การเขียนในรูปแบบดังกล่าวมักเรียกว่า
camel case นิยมใช้กับรูปแบบข้อมูลที่เป็นลักษณะการเขียนแบบ object เช่น
 
{
    display:'none',
    opacity:0,
    transform: 'translateY(100%)'
}
 
เวลากำหนดใน style ก็จะใช้เป็น
 
style({
    display:'none',
    opacity:0,
    transform: 'translateY(100%)'
})
 
การใช้งาน animation ใน angular เราสามารถสร้างไฟล์ class แล้ว import มาใช้งาน หรือจะใช้วิธีการ
กำหนดเข้าไปในส่วนของ @Component metadata ก็ได้ เราเคยได้ทดลองทำ animation ให้กับ component
ที่มีการเรียกใช้งานผ่าน router มาแล้วในตอนท้ายของบทความตามลิ้งค์ด้านล่าง ซึ่งเป็นการเรียกใช้งาน
จากไฟล์ animation class 
    การเรียก Route ผ่าน Router Outlet ที่มีการกำหนดชื่อ ใน Angular ตอนที่ 7 http://niik.in/847 
 
สำหรับการเรียกใช้งาน animation โดยกำหนดใน @Component metadata จะกำหนดในรูปแบบดังนี้
 
ไฟล์ home.component.ts บางส่วน
 
import { Component, OnInit } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition
} from '@angular/animations';

@Component({
  // selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
  animations:[]    
})
 
ในการใช้งาน animation เราจำเป็นต้องกำหนดตัวแปรสำหรับเป็น trigger หรือเข้าใจอย่างง่ายก็คือตัวที่เป็นไกปืน 
หรือสวิตท์สำหรับกำหนดอ้างอิงค่าของ state เข้ากับการทำงานการเคลื่อนไหว โดยปกติถ้าเป็น component 
ก็จะใช้ค่าตัวแปรที่เป็น property ของ component นั้นๆ โดยกำหนดในส่วนของ constructor() เช่น
 
export class MyComponent {
  constructor(public name: string, public state = 'inactive') { }

  toggleState() {
    this.state = this.state === 'active' ? 'inactive' : 'active';
  }
}
 
หรือถ้าเป็น กรณีเป็น directive หรือ element อื่นๆ ก็จะใช้ attribute object ในการกำหนด ดังนี้
สมมติเรากำหนดให้กับ <div> element ก็จะได้เป็น
 
<div [@myTrigger]="'inactive'">

</div>
 
กรณีกำหนดค่า state เป็น string เข้าไปเลย หรือ
 
<div [@myTrigger]="mystate">

</div>
 
กรณีใช้ค่าจาก property ของ component โดย mystate จะเป็น property ของ component ที่เราสามารถ
จะเปลี่ยนเแปลงค่าตามที่ต้องการเพื่อกำหนดเป็นค่า state ได้
 
มาลองตัวอย่างกัน เราจะสร้างตารางไว้ใน HomeComponent และมีปุ่มสำหรับเปลี่ยนค่า state ผ่านตัวแปร
mystateVal ที่เป็น component property โดยค่านี้จะเชื่อมโยงกับการกำหนด animation ผ่าน attribute
object หรือ trigger ที่ชื่อ myTrigger
 
ไฟล์ home.component.html
 
<p>  home works!</p>

<button class="btn btn-sm btn-primary" type="button" (click)="toggleTable()">
  {{ mystateVal=='inactive'?'Show':'Hide'}}
</button>
<br>
<table  class="table"  [@myTrigger]="mystateVal">
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">First</th>
      <th scope="col">Last</th>
      <th scope="col">Handle</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">1</th>
      <td>Mark</td>
      <td>Otto</td>
      <td>@mdo</td>
    </tr>
    <tr>
      <th scope="row">2</th>
      <td>Jacob</td>
      <td>Thornton</td>
      <td>@fat</td>
    </tr>
    <tr>
      <th scope="row">3</th>
      <td>Larry</td>
      <td>the Bird</td>
      <td>@twitter</td>
    </tr>
  </tbody>
</table>
 
จะเห็นว่าเรามีการกำหนด [@myTrigger]="mystateVal" ซึ่งเป็นการกำหนดการเชื่อมโยงการเคลื่อนไหว
กับ state ของ animation โดย myTrigger คือ attribute object ที่ใช้เป็น trigger รับค่า state จากตัวแปร
mystateVal แล้วไปเรียกใช้งาน animation ที่ต้องการ 
    เราใช้การคลิกปุ่ม ในการเปลี่ยนแปลงค่า state ของตัวแปร mystateVal โดยเมื่อคลิกที่ปุ่ม
ก็ให้ทำคำสั่ง  (click)="toggleTable()" ทำการสลับค่าไปมาระหว่าง 'inactive' กับ 'active' เข้าใจง่ายๆ ปุ่มนี้
ก็คือปุ่มสำหรับ ซ่อน หรือแสดงตาราง 
 
ต่อไปกำหนด animation และการทำงาน ในไฟล์ HomeComponent ดังนี้
 
ไฟล์ home.component.ts
 
import { Component, OnInit } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition
} from '@angular/animations';

@Component({
  // selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
  animations:[
    trigger('myTrigger', [
      state('inactive', style({
        display:'none',
        opacity: 0, 
        transform: 'translateX(-100%)' 
      })),      
      state('active', style({
        display:'table',
        opacity:1,
        transform: 'translateX(0)'
      })),
      transition('inactive => active', animate('200ms ease-in')),
      transition('active => inactive', animate('500ms ease-out')) 
    ])    
  ]    
})
export class HomeComponent implements OnInit {

  public mystateVal:string = 'inactive';

  constructor() {  }

  ngOnInit() {

  }

  toggleTable(){
    this.mystateVal = this.mystateVal=='active'?'inactive':'active';
  }
}
 
ของแยกส่วนของ animation ใน @Component metadata มาดังนี้
 
  animations:[
    trigger('myTrigger', [
      state('inactive', style(
      
      )),      
      state('active', style(
      
      )),
      transition('inactive => active', animate()),
      transition('active => inactive', animate()) 
    ])    
  ]    
 
รูปแบบการทำงานก็คือ ตรวจจับค่าจาก trigger ผ่านตัวแปรชื่อ myTrigger เพื่อเช็คค่า state ที่ได้จากตัวแปร
mystateVal ถ้าค่า state เป็น 'inactive' ก็กำหนด style เข้าไป style ที่กำหนดในแต่ละ state นั้น ก็คือรูปแบบ
การแสดงสุดท้ายที่เกิดขึ้น ในตัวอย่างเรามี state อยู่แค่สองแบบ ต่อจาก state ก็คือการกำหนด transition
หรือที่เรียกว่าการกำหนดการเปลี่ยนสภาวะของ state เช่นจาก 'inactive => active' หรือ จาก 'active => inactive'
ในการเปลี่ยนแปลงระหว่างสภาวะ เราก็กำหนด animate หรือรูปแบบการทำ animation การเคลื่อนไหวเข้าไป
นี่คือโครงสร้างการกำหนด animation คร่าวๆ
 
อธิบายผลลัพธ์การทำงาน เมื่อเราเข้ามายังหน้า home เนื่องจากเราให้ค่าเริ่มต้นของ mystateVal เป็น
'inactive' ก็จะแสดงเฉพาะปุ่ม แต่ตารางในสภาวะ state นี้จะไม่แสดง ตามที่กำหนดใน style
 
{
	display:'none', // ไม่แสดง
	opacity: 0, // โปร่งใส มองไม่เห็น
	transform: 'translateX(-100%)'  // ตำแหน่งแนวนอนอยู่ที่ติดลบ ตามค่าความกว้างของ table
}
 
แสดงดังรูป
 
 

 
 
เมื่อเราคลิกที่ปุ่ม Show ก็จะให้ค่าตัวแปร mystateVal เปลี่ยนจาก 'inactive' เป็น 'active' เมื่อค่า mystateVal เปลี่ยน 
ตัวแปร myTrigger ที่เป็น trigger ก็เกิดการทำงานและทำการ transition จากสภาวะ 'inactive => active' ทำ animate
ทำให้ตารางค่อยๆเลื่อนเข้ามาในตำแหน่งของ style ที่กำหนดให้กับ 'active' state โดยการ animate มีการกำหนดให้ใช้เวลา
ที่ 200 มิลลิวินาที (1000 มิลลิวินาที เท่ากับ 1 วินาที หรือ 1000ms = 1s)
    ในทางกลับกัน หลังจากที่ state เปลี่ยนเป็น 'active'  และตารางแสดงแล้ว ถ้าเราคลิกที่ปุ่ม Hide อีกครั้ง ค่าของตัวแปร
mystateVal ก็จะเปลี่ยนแจง 'active' เป็น 'inactive' ก็จะเกิด transition จากสภาวะ 'active => inactive' มีการ animate ทำ
ให้ตารางค่อยเลื่อนออกไปกลับไปยังตำแหน่งที่กำหนดใน 'inactive' style โดยใช้เวลา 500 มิลลิวินาที
 
 


 
 
สามารถดูตัวอย่างการเคลื่อนไหวได้ที่ demo 1 ท้าบบทความ
 
 

State และ Transition (การเปลี่ยนสภาวะของ State)

    การกำหนด State

    จากที่ได้อธิบายเบื้องต้นไปแล้ว ว่าการกำหนด style ให้กับ state ก็คือการกำหนดรูปแบบการแสดงสุดท้ายที่เกิดขึ้น 
หลังจากทำการ animation ใดๆ แล้ว หน้าตาของ element ที่มี state ใด ก็จะมี css style ตามที่กำหนด 
ดูส่วนของการกำหนด state เพิ่มเติม
 
  state('inactive', style({
    display:'none',
    opacity: 0, 
    transform: 'translateX(-100%)' 
  })),      
  state('active', style({
    display:'table',
    opacity:1,
    transform: 'translateX(0)'
  })),
 
transfrom คือรูปแบบของ css ที่ใช้ในการเปลี่ยนรูปร่างหรือตำแหน่งของ element สามารถดูเพิ่มเติมได้ที่
 
เมื่ออยู่ใน state 'inactive' ก็จะไม่มีการแสดง element นั้น มีการปรับเป็นโปร่งใส หรือให้มองไม่เห็น และมีการเปลี่ยน
ตำแหน่งไปยังตำแหน่ง -100% ซึ่งหมายถึง ถ้า element นั้นกว้าง 500px ตำแหน่ง -100% ก็คือ -500px เป็นตำแหน่ง
ที่อยู่ทางซ้ายมือในแนวนอน ที่ตำแหน่ง left position ของขอบเขต window ของ browser นั่นเอง
และเมื่ออยู่ใน state 'active' ก็จะแสดง element นั้นในรูปแบบของ table มีการปรับให้ไม่เป็นโปร่งใส หรือทำให้มองเห็นได้
และไม่มีการเปลี่ยนตำแหน่งใดๆ 
 

    การกำหนด Transition (การเปลี่ยนสภาวะของ State)

    หลังจากที่เราได้ทำการกำหนด state ที่ต้องการเรียบร้อยแล้ว เราสามารถกำหนด transition เพื่อกำหนดทิศทางการเปลี่ยน
สภาวะของ state ที่สร้างขึ้นมา โดยในแต่ละ transition จะควบคุมด้วยเวลาในการสลับไปมาระหว่าง การแสดงด้วย style หนึ่ง
กับอีก style ถัดไป กล่าวคือเวลาที่ทำการเปลี่ยน style ใน state กับ style ในอีก state นั่นเอง
 
พิจารณาส่วนของ transition ด้านล่างนี้
 
transition('inactive => active', animate('100ms ease-in')),
transition('active => inactive', animate('100ms ease-out'))
 
ดูทิศทางการสลับไปมาระหว่าง state ตามรูปด้านล่าง
 

 
 
ถ้าในหลายๆ transition มีการกำหนดเวลาเหมือนกันดังตัวอย่างด้านบน เราสามารถนำมารวมในการกำหนด
transition เดียวได้เป็นดังนี้
 
transition('inactive => active, active => inactive',
 animate('100ms ease-out'))
 
นอกจากนั้นถ้า transition สอง transition มีการกำหนดเวลาเหมือนกัน เราก็สามารถเขียนให้สั้นลงได้อีกด้วยการ
ใช้เครื่องหมาย <=> เป็นดังนี้
 
transition('inactive <=> active', animate('100ms ease-out'))
 
สำหรับค่า ease-in หรือ ease-out ที่กำหนดต่อจากส่วนของการกำหนดเวลา จะเรียกว่า css animation-timing-function
property อย่าง ease-in ก็คือมีการ animation เข้ามาช้าๆ ในตอนเริ่มต้น ส่วน ease-out ก็คือมีการ animation 
ช้าๆ ในตอนท้าย เป็นต้น สามารถดูเพิ่มเติม ได้ที่
 
เราทราบแล้วว่า transition จะทำการเปลี่ยนแปลง element style จาก state หนึ่งไปยังอีก state หนึ่ง นอกจากนี้ เรายังสามารถ
ที่จะแทรก style เข้าไปในขณะที่มีการทำ animate อยู่ด้วย ก็ได้ กล่าวคือ เราสามารถแทรก style เข้าไประหว่าง state ได้นั้นเอง
ตามรูปแบบดังนี้
 
transition('inactive => active', [
  style(
	// แทรกก่อนเริ่ม animation จะเริ่ม animation จาก style ที่กำหนดส่วนนี้
  ),
  animate('200ms ease-in', style(
	 // แทรกระหว่าง animation จะสิ้นสุด animation ที่ style นี้ก่อน แล้วไปจบที่ style ของ state
  ))
]),
 
นอกจากรูปแบบข้างต้น เรายังสามารถเลือกที่จะใช้งานการกำหนด style ในแต่ละจุดที่ต้องการก็ได้ เข่น
แทรกเฉพาะหว่าง animation
 
transition('inactive => active', [
  animate('200ms ease-in', style(
	 // แทรกระหว่าง animation จะสิ้นสุด animation ที่ style นี้ก่อน แล้วไปจบที่ style ของ state
  ))
]),
 
หรือแทรกเฉพาะก่อน animation แบบนี้
 
transition('inactive => active', [
  style(
	// แทรกก่อนเริ่ม animation จะเริ่ม animation จาก style ที่กำหนดส่วนนี้
  ),
  animate('200ms ease-in')
]),
 
 

การใช้งาน wildcard state *

    เราสามารถใช้งาน wildcard state ( * ) จับคู่กับ state ใดๆ ก็ได้ ซึ่งจะมีประโยชน์มากในกรณีที่ใช้ในการกำหนด style และ
transition ที่ไม่ได้ระบุ state ที่จะทำการ animation เข้าไป ยกตัวอย่าง
 
  • active => * เปลี่ยนสภาวะจาก 'active' state ไป สภาวะ state ใดๆ ก็ได้
  • * => * เปลี่ยนสภาวะจาก state ใดๆ ก็ได้ระหว่าง สอง state ที่เกิดขึ้น
 
ดูทิศทางการระหว่าง state กับการใช้งาน wildcard state * ตามรูปด้านล่าง
 
 
 

การใช้งาน void state

    สำหรับ void state นั้นจะเป็น state พิเศษเฉพาะที่ใช้สำหรับ animation ใดๆ โดยจะใช้กับ element ที่ยังไม่มีการนำ
มาแสดง หรือบางทียังไม่ได้เพิ่มเข้ามา หรืออาจจะถูกลบออกไปแล้ว ซึ่ง void จะมีประโยชน์สำหรับการใช้งานที่มีการกำหนด
enter animation และ leave animation  กล่าวคือเหมาะสำหรับการกำหนด animation การเพิ่มเข้ามาของ element และ
animation การลบออกไปของ element ยกตัวอย่าง
 
  • * => void เปลี่ยนสภาวะจาก state ใดๆ ไปสภาวะที่มีการ ลบออกจาก view
 
ดูทิศทางการระหว่าง state กับการใช้งาน void state ตามรูปด้านล่าง
 
 

 
 
การจับคู่ state ระหว่าง wildcard state * กับ void state ยังสามารถใช้ในการกำหนด transition สำหรับทำการ
animation การเพิ่มเข้ามาใน view และลบออกจาก view ของ element ได้ด้วย ตามรูปแบบดังนี้
 
  • Enter: void => *
  • Leave: * => void
 
ตัวอย่างการกำหนด Entering และ Leaving
 
animations: [
  trigger('flyInOut', [
    state('in', style({transform: 'translateX(0)'})),
    transition('void => *', [
      style({transform: 'translateX(-100%)'}),
      animate(300)
    ]),
    transition('* => void', [
      animate(300, style({transform: 'translateX(100%)'}))
    ])
  ])
]
 
เราสามารถใช้ชื่อแทน state ของรูปแบบทั้งสองด้วย :enter แทน void => * และ :leave แทน * => void
ปรับเป็นแบบใช้ชื่อแทนได้ดังนี้
 
animations: [
  trigger('flyInOut', [
    state('in', style({transform: 'translateX(0)'})),
    transition(':enter', [
      style({transform: 'translateX(-100%)'}),
      animate(100)
    ]),
    transition(':leave', [
      animate(100, style({transform: 'translateX(100%)'}))
    ])
  ])
]
 
เรายังสามารถใช้ void state ร่วมกับ state อื่นๆ ได้ ดังนี้ เช่น
 
  • enter เข้ามาแล้วเปลียนสภาวะเป็น inactive state: void => inactive
  • enter เข้ามาแล้วเปลียนสภาวะเป็น active state: void => active
  • เปลี่ยนจากสภาวะ inactive แล้ว leave ออกไป: inactive => void
  • เปลี่ยนจากสภาวะ active แล้ว leave ออกไป: active => void
 
ดูทิศทางการระหว่าง state กับการใช้งาน void state ตามรูปด้านล่าง
 

 
 
 

Property ที่สามารถทำ animation และหน่วยที่ใช้

เราสามารถใช้ css property ที่ browser กำหนดให้สามารถนำไปใช้ในการทำ animation ได้ ซึ่งประกอบไปด้วย
การกำหนดตำแหน่ง การปรับขนาด การเปลี่ยนรูปร่าง การเปลี่ยนสี การกำหนดเกี่ยวกับเส้นขอบ และอื่นๆ
สามารถดู css property ที่สามารถนำมาทำ animation ได้ที่
 
ในส่วนที่เกี่ยวกับ การระบุตำแหน่ง ที่ต้องมีการกำหนดค่าตัวเลข เราสามารถกำหนดค่าในรูปแบบของ string โดยต่อท้าย
ด้วยค่าหน่วยที่เหมาะสม เช่น '50px' หรือ '3em' หรือ '100%'
  ในกรณีที่มีการกำหนดตัวเลขเกี่ยวกับขนาด แต่ไม่ได้ระบุหน่วยลงไปด้วย เช่น 50 ลักษณะแบบนี้ angular จะให้ค่าหน่วย
เริ่มต้นเป็น px กล่าวคือ 50 มีค่าเท่ากับ '50px'

 

การคำนวณค่าของ property อัตโนมัติ

    ในบางครั้ง เราอาจจะไม่รู้ความกว้างหรือความสูงของขนาดของ element จนกว่าจะมีการทำงานเสร็จสิ้นแล้ว
ยกตัวอย่างเช่น element ที่บางครั้งจะมีขนาดความกว้างหรือความสูงขึ้นอยู่กับเนื้อหาและขนาดของหน้าจอแสดงผล
ซึ่งจะมีผลต่อการกำหนด animation ด้วย css
  ในกรณีข้างต้นนี้ เราสามารถใช้ ( * ) property เพื่อกำหนดเป็นค่าของ property นั้น โดยจะมีการคำนวณค่าที่เหมาะสม
ให้อัตโนมัติก่อนที่จะมีการนำเข้าไปใช้งานในการทำ animation
 
ยกตัวอย่าง leave animation ที่เกิดขึ้น ไม่ว่าความสูงของ element จะมีค่าเป็นเท่าไหร่ก็ตาม ก่อนที่มันจะออกไป โดย
ทำการ animation จากความสูงนั้นๆ ไปยังค่าที่เป็น 0
 
animations: [
  trigger('shrinkOut', [
    state('in', style({height: '*'})), // ความสูงใดๆ ก็ได้
    transition('* => void', [
      style({height: '*'}), // ก่อนลบออกไป มีความใดๆ ก็ได้
      animate(250, style({height: 0})) // animattion ไปที่ความสูงเท่ากับ 0
    ])
  ])
]
 

 

การกำหนด Animation timing

    เราได้เห็นการทำ animation โดยกำหนดคำสั่ง animate() เข้าไปใน transition ข้างต้นไปแล้ว ทีนี้เรามาดูว่า
ในการกำหนด animation timing เข้าไปใน animate() ฟังก์ชั่นนั้น มีค่าอะไรบ้าง
    ในการกำหนด animation timing นั้นจะประกอบไปด้วย 3 ค่า คือ 
 
  •     Duration (ระยะเวลาที่ทำการ animation) 
  •     Delay (เวลาที่ใช้ในการหน่วงจังหวะ ก่อนที่เริ่มทำ animation)
  •     Easing (เป็น easing function หรือรูปแบบการเคลือนไหวที่มีลักษณะกำหนดความเร็วความช้าเข้าในในช่วงจังหวะเวลา)
 

    วิธีการกำหนด Duration

    รูปแบบที่ใช้ในการกำหนดเวลาที่ใช้ในการทำ animation จากเริ่มต้นจนสิ้นสุดนั้น เราสามารถกำหนดได้ใน 3 รูปแบบ ดังนี้
 
เป็นตัวเลข ไม่มีระบุค่าหน่วย ซึ่งจะมีค่าเท่ากับ มิลลิวินาที เช่น: animate(100)
เป็นข้อความ หน่วย มิลลิวินาที เช่น: animate('100ms')
เป็นข้อความ หน่วย วินาที เช่น: animate(''0.1s'')
 

    วิธีการกำหนด Delay

    รูปแบบที่ใช้ในการกำหนดเวลาที่ค่าจาก trigger มีการเปลี่ยนแปลงก่อนที่จะเริ่ม animation จะกำหนดในรูปแบบเช่น
เดียวกับการกำหนด Duration โดยให้กับหนดต่อจาก ค่า Duration ยกตัวอย่างเช่น
 
    animate('0.2s 100ms') // รอ 100 มิลลิวินาที ก่อนจะทำ animation ต่ออีกเป็นเวลา 200 มิลลิวินาที
 

    วิธีการกำหนด Easing

    รูปแบบการเคลือนไหวที่มีลักษณะกำหนดความเร็วความช้าเข้าในในช่วงจังหวะเวลา หรือที่เรียกว่า css animation-timing-function property อย่าง ease-in ก็คือมีการ animation เข้ามาช้าๆ ในตอนเริ่มต้น ส่วน ease-out ก็คือมีการ animation 
ช้าๆ ในตอนท้าย เป็นต้น สามารถดูเพิ่มเติม ได้ที่
    ยกตัวอย่างเช่น
 
    animate('0.2s 100ms ease-out') // มีการกำหนด delay เท่ากับ 100ms
    animate('0.2s 100ms') // ไม่มีการกำหนด delay
 

 

การกำหนด animation ด้วย keyframe

    เราสามารถใช้งาน keyframe ในการกำหนดการเปลี่ยนสภาวะที่มีรูปแบบง่ายเป็นรูปแบบที่มีความซับซ้อนขึ้น โดยทำการ
เปลี่ยนจาก style หนึ่งไปยังอีก style หนึ่ง หรือเปลี่ยนไปยัง style ที่แทรกอยู่กรณีมีการกำหนดมากกว่าสอง style
    ในแต่ละ keyframe จะใช้ค่า offset เป็นตัวกำหนดจุดของ keyframe ที่จะทำการเคลื่อนไหว ซึ่งจะมีค่าตะหว่าง 0 ถึง 1
โดยการเคลื่อนไหวจะเริ่มต้นที่ offset เท่ากับ 0 และสิ้นสุดที่ offset เท่ากับ 1 หากมีการกำหนด keyframe ระหว่างสองค่านี้
ก็จะใช้เป็นตัวเลขทศนิยม เช่น 0.1 0.2 0.3.... ลักษณะแบบนี้เป็นตัว
 
ดูตัวอยางการใช้งาน keyframe จะมีรูปแบบดังนี้
 
animations: [
  trigger('flyInOut', [
    state('in', style({transform: 'translateX(0)'})),
    transition('void => *', [
      animate(300, keyframes([
        style({opacity: 0, transform: 'translateX(-100%)', offset: 0}),
        style({opacity: 1, transform: 'translateX(15px)',  offset: 0.3}),
        style({opacity: 1, transform: 'translateX(0)',     offset: 1.0})
      ]))
    ]),
    transition('* => void', [
      animate(300, keyframes([
        style({opacity: 1, transform: 'translateX(0)',     offset: 0}),
        style({opacity: 1, transform: 'translateX(-15px)', offset: 0.7}),
        style({opacity: 0, transform: 'translateX(100%)',  offset: 1.0})
      ]))
    ])
  ])
]
 
 
ในการใช้งาน keyframe เราต้องทำการ import keyframe เข้ามาใช้งานก่อนเรียกใช้ด้วย ดังนี้
 
import {
  trigger,
  state,
  style,
  animate,
  transition,
  keyframes
} from '@angular/animations';
 
การกำหนด offset ให้กับ keyframe นั้น ไม่ใช้รูปแบบการกำหนดที่อ้างอิงกับเวลา   แต่เป็นรูปแบบที่สัมพันธ์กับกับค่าระหว่าง 0 ถึง 1
ดังนั้น การเคลื่อนไหวที่เกิดขึ้น ก็ยังอิงกับค่าการกำหนด duration delay และ easing   ค่า offset นี้เป็นค่าที่เป็น optiion จะกำหนดหรือไม่
ก็ได้ หากไม่ได้กำกหนด angular จะทำการกำหนดค่าให้โดยอัตโนมัติ ยกตัวอย่างเช่น กรณีมี 3 keyframe ค่า offset อัตโนมัติก็จะเป็น
0,  0.5 และ 1 แบบนี้เป็นต้น
 
 

การใช้งาน Parallel animation group

    เราสามารถใช้ animation group ในการกำหนดเวลา ให้กับ animation ที่เกิดขึ้นพร้อมกันแบบ parallel หรือแบบคู่ขนานได้
โดยใช้งาน group โดยก่อนใช้งานต้องทำการ import group เข้ามาใช้งานก่อนดังนี้
 
import {
  trigger,
  state,
  style,
  animate,
  transition,
  keyframes,
  group
} from '@angular/animations';
 
    ยกตัวอย่างการใช้งาน เช่น เราต้องการกำหนดการเคลื่อนไหวให้กับ css property สองอัน โดยใช้งาน easing function ที่แตกต่าง
กัน ในแต่ละ animation ดูตัวอย่างการใช้งานตามโค้ดด้านล่าง
 
animations: [
  trigger('flyInOut', [
    state('in', style({width: 120, transform: 'translateX(0)', opacity: 1})),
    transition('void => *', [
      style({width: 10, transform: 'translateX(50px)', opacity: 0}),
      group([
        animate('0.3s 0.1s ease', style({
          transform: 'translateX(0)',
          width: 120
        })),
        animate('0.3s ease', style({
          opacity: 1
        }))
      ])
    ]),
    transition('* => void', [
      group([
        animate('0.3s ease', style({
          transform: 'translateX(50px)',
          width: 10
        })),
        animate('0.3s 0.2s ease', style({
          opacity: 0
        }))
      ])
    ])
  ])
]
 
 
เราจะเห็นว่า ใน group จะมีการกำหนด animation สองรูปแบบ แบบแรกทำการเปลี่ยนรูปร่างและขนาดความกว้าง ส่วนอีกแบบ
ทำการเปลี่ยน opacity หรือความโปร่งใส  โดยทั้งสองรูปแบบเริ่มทำงานพร้อมกัน
 
 

การกำหนด Animation callback

    เราสามารถใช้งาน animation callback เพื่อให้ทำงานคำสั่งใด ที่ต้องการเมื่อเริ่มมีการทำ animatiion หรือเมื่อสิ้นสุดการทำ 
animation โดยจะส่งค่า AnimationEvent เป็น parameter เข้าไปในฟังก์ชั่นที่เรากำหนดเป็น callback ใน AnimationEvent
จะประกอบไปด้วยค่าต่างๆ ตามรูปแบบ interface ของ AnimationEvent ดังนี้
 
interface AnimationEvent { 
  fromState: string // 
  toState: string
  totalTime: number
  phaseName: string
  element: any
  triggerName: string
}
 
 
ตัวอย่างการใช้งาน 
 
ไฟล์ home.component.html
 
<p>  home works!</p>
<br>
<table  class="table">
  <thead>
    <tr>
      <th scope="col">#</th>
      <th scope="col">First</th>
      <th scope="col">Last</th>
      <th scope="col">Handle</th>
    </tr>
  </thead>
  <tbody>
    <tr *ngFor="let member of members;let i=index"
    (@flyInOut.start)="animationStarted($event)"
    (@flyInOut.done)="animationDone($event)"   
    [@flyInOut]="'in'">
      <td>{{ i+1 }}</td>
      <td>{{ member.name }}</td>
      <td>{{ member.lastname }}</td>
      <td>{{ member.handle }}</td>
    </tr>
  </tbody>
</table>
 
ไฟล์ home.component.ts
 
import { Component, OnInit } from '@angular/core';
import {
  trigger,
  state,
  style,
  animate,
  transition,
  keyframes,
  group
} from '@angular/animations';

@Component({
  // selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css'],
  animations: [
    trigger('flyInOut', [
      state('in', style({transform: 'translateX(0)'})),
      transition('void => *', [
        style({transform: 'translateX(-100%)'}),
        animate(300)
      ]),
      transition('* => void', [
        animate(300, style({transform: 'translateX(100%)'}))
      ])
    ])
  ]
})
export class HomeComponent implements OnInit {

  public members:Person[];

  constructor() {  }

  ngOnInit() {
    this.members = [
      {name:'Mark',lastname:'Otto',handle:'	@mdo'},
      {name:'Jacob',lastname:'Thornton',handle:'@fat'},
      {name:'Larry',lastname:'the Bird',handle:'@twitter'}
    ]
  }


  animationStarted(e:any){
    console.log('animationStarted');
    console.log(e);
  }  

  animationDone(e:any){
    console.log('animationDone');
    console.log(e);
  }    
}

interface Person{
  name:string,
  lastname:string,
  handle:string
}
 
 
สำหรับเนื้อหาเกี่ยวกับการใช้งาน animation ใน angular ทั้งหมดโดยประมาณก็จะมีเนื้อหาประมาณนั้ การใช้งานต่างๆ ขึ้นกับการนำไป
ประยุกต์เพิ่มเติม 
 
เนื้อหานี้อาจจะถือได้ว่าเป็นเนื้อหาท้ายๆ เกี่ยวกับ angular แล้ว แต่อย่างไรก็ตาม หากมีลูกเล่น หรือแนวทางการประยุกต์ หรือแม้แต่
การอัพเดทเพิ่มเติม ก็จะได้นำมานำเสนอในลำดับต่อๆ ไป 
 
สำหรับเนื้อหาต่อไป อาจจะเป็นการประยุกต์ใช้งานเกี่ยวกับ angular ไม่ว่าจะเป็นการใช้งาน ionic native เพื่อสร้างเป็น mobile app
หรือการสร้างเป็น desktop app ด้วย electron แต่ก็ขึ้นกับทิศทางหรือความน่าสนใจในอนาคตด้วย อย่าลืมกด Like เป็นกำลังใจ สำหรับ
บทความผ่านๆ มา และบทความที่อาจจะมีขึ้นในอนาคตต่อๆ ไป   (^__^)
 




กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ











URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ