Example 1
CSS - Hướng dẫn: Tạo ảnh động với hàm steps()
Cập nhật: 10/08/2018 Lượt xem: 909Tạo hiệu ứng Animation trong CSS với hàm steps(). Chức năng này rất hữu ích cho việc tạo hoạt ảnh trang tính Sprite vì chúng ta có thể hiển thị chính xác từng hình ảnh sprite dưới dạng khung mà không có bất kỳ hiệu ứng gián đoạn nào.
Có một chức năng ít được biết đến trong Aniamtion CSS cho phép chúng ta chia hoạt ảnh thành các phân đoạn - hoặc các bước – thay vì chạy nó dưới dạng hoạt ảnh liên tục từ đầu đến cuối. Chức năng này rất hữu ích cho việc tạo hoạt ảnh trang tính Sprite vì chúng ta có thể hiển thị chính xác từng hình ảnh sprite dưới dạng khung mà không có bất kỳ hiệu ứng gián đoạn nào.
Animation với hàm steps()
Với steps(), chúng ta có thể kiểm soát số lượng khung hình chính được hiển thị trong thời lượng hoạt ảnh; nó tiến hành hoạt ảnh theo các bước đều nhau dựa trên giá trị chúng ta đặt ra. Biết được điều này, chúng ta hãy sử dụng steps() để tạo ra một hoạt ảnh nhân vật đơn giản.
Tôi đã sử dụng các Illustrator để tạo mỗi khung hình hoạt ảnh dưới dạng hình ảnh 190 × 240 riêng biệt, sau đó tận dụng tính năng sprace của Compass để nhanh chóng tạo ra một trang sprite ngang chứa tất cả các hình ảnh đã xuất.
Tạo hoạt ảnh
Để tạo hiệu ứng cho nhân vật quái vật của chúng ta, trước tiên chúng ta sẽ tạo quy tắc trong đó chúng ta xác định kích thước chiều rộng và chiều cao và hiển thị trang ma trận chính dưới dạng hình nền.
Copy .monster {
width: 190px;
height: 240px;
background: url('monster-sprite.png') left center;
}
Tiếp theo, chúng ta cần phải tạo một quy tắc keyframe để làm động vị trí nền của trang ma trận. Chiều rộng tổng của trang ma trận là 1900px, vì vậy hãy tạo hiệu ứng động từ phải sang trái bằng cách đặt nó ở vị trí nền cuối cùng là -1900px.
Copy @keyframes play {
100% { background-position: -1900px; }
}
Chạy hoạt ảnh
Tại thời điểm này, khi chúng ta liên kết chuỗi hoạt ảnh play với bộ chọn .monster với thời lượng là 8 giây, chúng ta thấy vị trí nền của trang ma trận của chúng ta nhanh chóng hoạt hình từ trái sang phải.
Copy .monster {
...
animation: play 0.8s;
}
Để đạt được hiệu ứng hoạt hình theo từng khung hình mong muốn, chúng tôi sẽ cần phải thiết lập thêm hàm steps() trong value của animation. Vì trang tính có chứa 10 hình ảnh, chúng ta có thể nói rằng nó được tạo thành từ 10 khung hình - hoặc các bước. Vì vậy, hãy xác định 10 bước trong chuỗi hoạt ảnh của chúng ta:
Copy .monster {
...
animation: play 0.8s steps(10);
}
Giờ đây, hoạt ảnh sẽ chạy 10 khung hình trong khoảng thời gian .8s - nó sử dụng hoạt ảnh ở vị trí nền để chạy qua từng hình ảnh sprite dưới dạng một bước.
Cuối cùng, nếu chúng ta đặt animation-iteration-count thành infinite, nó sẽ hiển thị một vòng lặp lặp lại của hoạt ảnh.
Copy .monster {
...
animation: play 0.8s steps(10) infinite;
}
Để thay đổi tốc độ của hoạt ảnh, chỉ cần thay đổi giá trị của animation-duration. Dưới đây là chuỗi hoạt ảnh trang tính sprite cuối cùng được đăng trên CodePen:
CSS thật thú vị, bạn có muốn học nó không?
Example 2
Copy <! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title >Document</ title >
< link rel = "stylesheet" href = "1.css" >
</ head >
< body >
< div class = "monster" ></ div >
</ body >
</ html >
Copy .monster {
width : 50 px ;
height : 72 px ;
background : url (http://openbookproject.net/thinkcs/python/english3e/_images/duke_spritesheet.png)
left top ;
transition : background 0.5 s steps (9 , end) ;
& :hover {
background-position : -450 px top ;
}
}
Example 3
C:\xampp82\htdocs\html\1.html
Copy <! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title >Document</ title >
< link rel = "stylesheet" href = "1.css" >
</ head >
< body >
< div class = "animatedImage" ></ div >
</ body >
</ html >
Copy .animatedImage {
background-image : url (https://i.stack.imgur.com/kuEev.png) ;
height : 300 px ;
width : 500 px ;
animation : png-animation 1 s steps (3) forwards ;
animation-iteration-count : infinite ;
}
@keyframes png-animation {
to {
background-position : -1500 px 0 px ;
}
}
C:\xampp82\htdocs\html\1.html
Copy <! DOCTYPE html >
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
< title >Document</ title >
< link rel = "stylesheet" href = "1.css" >
</ head >
< body >
< div class = "stage" >
< div class = "ken stance" ></ div >
</ div >
< div class = "commands" >
< h1 >Control Ken with ur keyboard</ h1 >
Punch: < button id = "a" >a</ button >< br >
Kick: < button id = "z" >z</ button >< br >
Reverse kick: < button id = "e" >e</ button >< br >
< br >
Tatsumaki: < button id = "q" >q</ button >< br >
Hadoken: < button id = "s" >s</ button >< br >
Shoryuken: < button id = "d" >d</ button >< br >
< br >
Jump: < button id = "up" >▲</ button >< br >
Walk: < button id = "left" >◀</ button >< button id = "right" >►</ button >< br >
Kneel: < button id = "down" >▼</ button >
</ div >
</ body >
</ html >
C:\xampp82\htdocs\html\1.scss
Copy @mixin anim ($animName, $steps, $animNbr, $animParams) {
.#{$animName} {
@content ;
animation : $animName steps ($steps) $animParams ;
}
@keyframes #{$animName} {
from {
background-position : 0 px ( - $spriteHeight * ($animNbr - 1)) ;
}
to {
background-position : - ($spriteWidth * $steps) ( - $spriteHeight * ($animNbr -
1)) ;
}
}
}
/* element who's going to receive this class will be mirrored */
.flip {
transform : scaleX (-1) ;
}
/* sprite tile dimensions */
$spriteWidth : 70 px ;
$spriteHeight : 80 px ;
/* ken */
.ken {
position : absolute ;
bottom : 112 px ;
margin-left : 150 px ;
width : $spriteWidth ;
height : $spriteHeight ;
background-image : url ( "https://raw.githubusercontent.com/jkneb/street-fighter-css/master/images/ken.png" ) ;
/* other sprites preloading */
& :before {
content : "" ;
background: url("https://raw.githubusercontent.com/jkneb/street-fighter-css/master/images/ken-tatsumaki-senpuu-kyaku.png")
no-repeat ;
}
& :after {
content : "" ;
background : url ( "https://raw.githubusercontent.com/jkneb/street-fighter-css/master/images/ken-shoryuken.png" )
no-repeat ;
}
}
/* ken's fireball */
@include anim (
$animName : fireball,
$steps : 2 ,
$animNbr : 5 ,
$animParams : 0.15 s infinite
) {
@extend .ken ;
position : absolute ;
left : 100 % ;
bottom : 0 px ;
margin-left : 0 ; /* default margin-left */
background-position : 140 px 320 px ; /* default background position */
transition : margin 8 s linear ;
& :before {
left : 25 px ;
right : 25 px ;
}
& .moving {
margin-left : 4000 px ;
} /* triggering the movement with this class */
}
/* ken's fireball impact explosion */
@include anim ($animName : explode, $steps : 4 , $animNbr : 6 , $animParams : 0.5 s 1 );
/* stance */
@include anim (
$animName : stance,
$steps : 4 ,
$animNbr : 2 ,
$animParams : 0.5 s infinite
);
/* hadoken - must be declared AFTER .stance */
@include anim (
$animName : hadoken,
$steps : 4 ,
$animNbr : 1 ,
$animParams : 0.5 s infinite
);
/* punch */
@include anim (
$animName : punch,
$steps : 3 ,
$animNbr : 3 ,
$animParams : 0.15 s infinite
);
/* walking */
@include anim (
$animName : walk,
$steps : 5 ,
$animNbr : 4 ,
$animParams : 0.5 s infinite
);
/* kick */
@include anim (
$animName : kick,
$steps : 5 ,
$animNbr : 7 ,
$animParams : 0.5 s infinite
);
/* reverse kick */
@include anim (
$animName : reversekick,
$steps : 5 ,
$animNbr : 8 ,
$animParams : 0.5 s infinite
);
/* kneel down */
@include anim (
$animName : kneel,
$steps : 1 ,
$animNbr : 10 ,
$animParams : 0.2 s infinite
);
/* jump */
@include anim (
$animName : jump,
$steps : 7 ,
$animNbr : 9 ,
$animParams : 1 s infinite
) {
transition : bottom 0.5 s cubic-bezier (0.99 , 0.005 , 0 , 0.42) ;
bottom : 225 px ;
& .down {
bottom : 112 px ;
}
}
/* shoryuken */
@include anim (
$animName : shoryuken,
$steps : 7 ,
$animNbr : 1 ,
$animParams : 1 s infinite
) {
height : 110 px ;
background-image : url ( "https://raw.githubusercontent.com/jkneb/street-fighter-css/master/images/ken-shoryuken.png" ) ;
transition : bottom 0.5 s cubic-bezier (0.99 , 0.005 , 0 , 0.42) ;
bottom : 225 px ;
& .down {
bottom : 112 px ;
}
}
/* tatsumaki senpuu kyaku */
@include anim (
$animName : tatsumaki,
$steps : 13 ,
$animNbr : 1 ,
$animParams : 2 s infinite
) {
height : 110 px ;
background-image: url("https://raw.githubusercontent.com/jkneb/street-fighter-css/master/images/ken-tatsumaki-senpuu-kyaku.png");
transition : bottom 0.2 s cubic-bezier (0.99 , 0.005 , 0 , 0.42) ;
bottom : 132 px ;
& .down {
bottom : 112 px ;
}
}
.commands {
h1 {
margin-top : 0 ;
font-size : 14 px ;
}
margin-left : 450 px ;
font-size : 12 px ;
padding : 15 px ;
i {
padding : 0 px 4 px ;
font-style : normal ;
font-weight : bold ;
outline : 1 px solid pink ;
}
button + button {
white-space : nowrap ;
}
}
.stage {
position : absolute ;
top : 0 ;
width : 450 px ;
height : 330 px ;
background : url ( "https://raw.githubusercontent.com/jkneb/street-fighter-css/master/images/bg2.jpg" )
no-repeat 0 px -100 px ;
background-size : contain ;
float : left ;
margin-right : 20 px ;
}
body {
margin : 0 px ;
padding : 0 ;
}
C:\xampp82\htdocs\html\1.js
Copy var $ken = $ ( '.ken' );
var $kenPos , $fireballPos;
var punch = function () {
$ken .addClass ( 'punch' );
setTimeout ( function () { $ken .removeClass ( 'punch' ); } , 150 );
};
var kick = function () {
$ken .addClass ( 'kick' );
setTimeout ( function () { $ken .removeClass ( 'kick' ); } , 500 );
};
var rkick = function () {
$ken .addClass ( 'reversekick' );
setTimeout ( function () { $ken .removeClass ( 'reversekick' ); } , 500 );
};
var tatsumaki = function () {
$ken .addClass ( 'tatsumaki' );
setTimeout ( function () { $ken .addClass ( 'down' ); } , 1500 );
setTimeout ( function () { $ken .removeClass ( 'tatsumaki down' ); } , 2000 );
};
var hadoken = function () {
$ken .addClass ( 'hadoken' );
setTimeout ( function () { $ken .removeClass ( 'hadoken' ); } , 500 );
setTimeout ( function () {
var $fireball = $ ( '<div/>' , { class : 'fireball' });
$fireball .appendTo ($ken);
var isFireballColision = function () {
return $fireballPos .left + 75 > $ (window) .width () ? true : false ;
};
var explodeIfColision = setInterval ( function () {
$fireballPos = $fireball .offset ();
//console.log('fireballInterval:',$fireballPos.left);
if ( isFireballColision ()) {
$fireball .addClass ( 'explode' ) .removeClass ( 'moving' ) .css ( 'marginLeft' , '+=22px' );
clearInterval (explodeIfColision);
setTimeout ( function () { $fireball .remove (); } , 500 );
}
} , 50 );
setTimeout ( function () { $fireball .addClass ( 'moving' ); } , 20 );
setTimeout ( function () {
$fireball .remove ();
clearInterval (explodeIfColision);
} , 3020 );
} , ( 250 ));
};
var shoryuken = function () {
$ken .addClass ( 'shoryuken' );
setTimeout ( function () { $ken .addClass ( 'down' ); } , 500 );
setTimeout ( function () { $ken .removeClass ( 'shoryuken down' ); } , 1000 );
};
var jump = function () {
$ken .addClass ( 'jump' );
setTimeout ( function () { $ken .addClass ( 'down' ); } , 500 );
setTimeout ( function () { $ken .removeClass ( 'jump down' ); } , 1000 );
};
var kneel = function () {
$ken .addClass ( 'kneel' );
};
var walkLeft = function () {
$ken .addClass ( 'walk' ) .css ({ marginLeft : '-=10px' });
};
var walkRight = function () {
$ken .addClass ( 'walk' ) .css ({ marginLeft : '+=10px' });
};
// on click events
$ ( '#a' ) .click (punch);
$ ( '#z' ) .click (kick);
$ ( '#e' ) .click (rkick);
$ ( '#q' ) .click (tatsumaki);
$ ( '#s' ) .click (hadoken);
$ ( '#d' ) .click (shoryuken);
$ ( '#up' ) .click (jump);
$ ( '#down' ) .on ( 'mousedown mouseup' , function (e) {
if ( e .type == 'mousedown' ) { kneel (); }
else { $ken .removeClass ( 'kneel' ); }
});
$ ( '#left' ) .on ( 'mousedown mouseup' , function (e) {
if ( e .type == 'mousedown' ) { walkLeft (); }
else { $ken .removeClass ( 'walk' ); }
});
$ ( '#right' ) .on ( 'mousedown mouseup' , function (e) {
if ( e .type == 'mousedown' ) { walkRight (); }
else { $ken .removeClass ( 'walk' ); }
});
// on keydown events
$ (document) .on ( 'keydown keyup' , function (e) {
if ( e .type == 'keydown' ) {
// s - hadoken
if ( e .keyCode == 83
&& ! $ken .hasClass ( 'tatsumaki' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'punch' )
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'reversekick' )
) {
hadoken ();
}
// d - shoryuken
if ( e .keyCode == 68
&& ! $ken .hasClass ( 'tatsumaki' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'punch' )
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'reversekick' )
&& ! $ken .hasClass ( 'jump' )
) {
shoryuken ();
}
// q - tatsumaki senpuu kyaku
if ( e .keyCode == 81
&& ! $ken .hasClass ( 'tatsumaki' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'punch' )
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'reversekick' )
&& ! $ken .hasClass ( 'jump' )
) {
tatsumaki ();
}
// a - punch
if ( e .keyCode == 65
&& ! $ken .hasClass ( 'punch' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'tatsumaki' )
) {
punch ();
}
// e - kick
if ( e .keyCode == 90
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'tatsumaki' )
) {
kick ();
}
// r - reverse kick
if ( e .keyCode == 69
&& ! $ken .hasClass ( 'reversekick' )
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'tatsumaki' )
) {
rkick ();
}
// up - jump
if ( e .keyCode == 38
&& ! $ken .hasClass ( 'jump' )
&& ! $ken .hasClass ( 'reversekick' )
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'tatsumaki' )
) {
jump ();
}
// down - kneel
if ( e .keyCode == 40
&& ! $ken .hasClass ( 'kneel' )
&& ! $ken .hasClass ( 'jump' )
&& ! $ken .hasClass ( 'reversekick' )
&& ! $ken .hasClass ( 'kick' )
&& ! $ken .hasClass ( 'hadoken' )
&& ! $ken .hasClass ( 'shoryuken' )
&& ! $ken .hasClass ( 'tatsumaki' )
) {
kneel ();
}
// ← flip
//if (e.keyCode == 37) $ken.addClass('flip');
// → unflip
//if (e.keyCode == 39) $ken.removeClass('flip');
// ←← →→ walking
if ( e .keyCode == 37 ) { walkLeft (); }
if ( e .keyCode == 39 ) { walkRight (); }
}
else { // keyup
$ken .removeClass ( 'walk kneel' );
}
return false ;
//console.log(e.keyCode);
});
: