ホームページ >ウェブフロントエンド >htmlチュートリアル >モバイル淘宝正月グッズ祭りオープニングの龍舞アニメーション_html/css_WEB-ITnose
モバイルタオバオユーザーの皆さんは、ここ数日で新年祭りバージョンをご覧になったはずですが、初めてホームページを開いたとき、銅鑼、太鼓、爆竹の音に怯えたでしょうか。新年の雰囲気を演出するために。 PD は新年祭りのドラゴン ダンスのオープニング アニメーションを作成しました。このタスクは、モバイル端末に .gif アニメーションを実装するのに非常に骨が折れました。そこで今日は、このアニメーション効果を実現する方法を紹介します。
Web アニメーションは PC では難しくなく、クライアントが提供するアニメーション特殊効果も非常にスムーズなので、次の .gif アニメーション効果をモバイル端末に実装するのは 2 回目です (前回。 1 つはクリスマスの公開アニメーションです)。
初めてこの効果を見たとき、私は少し罪悪感を感じ、少し酔ったように感じました。実際、当初は .gif アニメーションを直接使用する予定でしたが、.gif アニメーションの使用には次の 2 つの問題があります:
それでは、どうやって行うのでしょうか?やってみようという気持ちで、このアニメーションの旅を始めました。
アニメーション全体は 2 つのシーンに分かれています。それでは、これら 2 つのシーンを簡単に分析してみましょう:
お披露目アニメーションには静的マスクが付いています:
この画面には次のアクションがあります:
アニメーションが 2 番目のシーンに入ると、アニメーション全体に次のアクション:
アニメーション全体は、CSSアニメーションのアニメーションプロパティを使用して行われます。ここでは主にアニメーションのsteps()のアニメーションタイミング関数を使用します。実際、これはマルチステップ アニメーションであり、スプライト イメージとアニメーション内のsteps()を組み合わせることで、次のようなアニメーション効果を簡単に実現できるため、スプライト イメージは主にマルチステップ アニメーションで使用されます。
アニメーションのキャラクター全体が常に動いており、アクション間の変化が非常に調和していることがわかります。
アニメーションシーン全体とその実装原則を理解したところで、具体的な制作プロセスと、制作プロセス中にどのような落とし穴に遭遇するかを見てみましょう。
それ以外については話さないでください。まず、アニメーション全体の効果を携帯電話でスキャンしてください:
(^_^)騙されないでください。銅鑼と太鼓の音怖い。
アニメーション全体を 1 つのシーンに置き、それを「ステージ」と呼び、このステージに Dragon-poplayer という名前を付けます:
<div class="dragon-poplayer"></div>
アニメーションには 2 つのシーンがあるので、このシーンを「コンテナ」の場合:
<div class="dragon-poplayer" id="dragon-poplayer"> <div class="dragon-section dragon-ready-play" id="dragon-ready-play"> <div class="dragon-play"> <!-- 第一场景 --> </div> </div> <div class="dragon-section dragon-playing" id="dragon-playing"> <!-- 第二场景 --> </div></div>
ユーザーがアニメーション全体をより適切に制御できるようにするために、結局のところ、すべてのユーザーがそれを好むわけではありませんが、ステージの同じレベルに閉じるボタンが追加されます:
<div id="close"></div>
前述したように、最初のシーンには主にミュート ボタンがありますそして 2 番目のシーンをトリガーするアクション ボタン (再生ボタンと呼びます)。もう 1 つは、音楽 b97864c2e0ef2353a16c4d64c7734e92 をこのコンテナに投入することです。
ミュートボタンをよりパーソナライズするために、ここではシミュレートされたチェックボックスを使用しています(具体的な作成方法については、「CSS3でiPhoneのチェックボックスを作成する」を参照してください)。
<div id="close"></div>
2 番目のシーンでは、まず、ドラゴン全体が 5 つの部分に分かれており、それぞれが 5 人の子供によって保持されています。ドラゴンをより適切に制御し、より上手に踊るために、ドラゴン全体が 5 つの部分に分かれています。制御する div によって保持されます:
<div class="dragon-wrap"> <div class="dragon-content"> <div class="dragon dragon1"></div> <div class="dragon dragon2"></div> <div class="dragon dragon3"></div> <div class="dragon dragon4"></div> <div class="dragon dragon5"></div> </div></div>
ドラゴンの周りに 3 つの雲が浮かんでいます。それぞれの雲も独立した
<div class="dragon-wrap"> <div class="dragon-content"> <div class="dragon dragon1"></div> <div class="dragon dragon2"></div> <div class="dragon dragon3"></div> <div class="dragon dragon4"></div> <div class="dragon dragon5"></div> <section class="cloud"></section> <section class="cloud"></section> <section class="cloud"></section> </div></div>
言うまでもなく、2 つの div を使用します。配置先:
<div class="firecrackers firecrackers-left"></div><div class="firecrackers firecrackers-right"></div>
最終的な HTML は次のようになります:
<div id="close"></div>
ステージ全体が画面全体に表示されます。 まず、HTML、ボディ、ステージの Dragon-poplayer を全画面モードに設定します:
html,body { height: 100vh; min-width: 10rem; margin-left: auto; margin-right: auto; background: transparent;}body { min-height: 100%; background: url(http://gw.alicdn.com/mt/TB1.sknLXXXXXbEXpXXXXXXXXXX-750-1333.png) no-repeat; background-size: 10rem 100%;}.dragon-poplayer,.dragon-section { position: absolute; top: 0; left: 0; right: 0; bottom: 0; width: 10rem; height: 100%; overflow: hidden;}
実際には、 3 番目のシーンのスタイルは非常に単純です。ここでは詳しく説明しません。参考までにコードを掲載します。
.dragon-play{ width: 10rem; height: 10.946667rem; //821px background: url('//gw.alicdn.com/mt/TB13eupLpXXXXaGXXXXXXXXXXXX-750-821.png') no-repeat center; background-size: 10rem 10.946667rem; position: absolute; z-index: 10; .music { position: absolute; width: 1.866667rem; //140 height: 0.533333rem; //40px top: 3.6rem; //270px left: 4.266667rem; //320px z-index: 12; input[type="checkbox"]{ opacity: 0; &:checked + label:before { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8AAAAoCAMAAABZ/...'); } } label { white-space: nowrap; display: block; position: absolute; top: -0.026667rem; //2px left: 0; font-size: 0; width: 100%; height: 0.533333rem; //40px &:before { content: ""; display: inline-block; width: 0.626667rem; //47px height: 0.533333rem; //40px background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC8A...') no-repeat; background-size: 0.626667rem 0.533333rem; //47px 40px } } } @at-root #music { position: absolute; width: 100%; height: 100%; top: 0; left: 0; right: 0; bottom: 0; background-color: transparent; cursor: pointer; }}
ユーザーが [再生] をクリックすると、最初のシーンから 2 番目のシーンに入ります。プロセス中にアニメーションが発生し、最初のシーンがゆっくりとフェードアウトし (フェードアウト)、2 番目のシーンがゆっくりとアニメーションにフェードインします:
.dragon-ready-play{ z-index: 100; &.is-animationed { animation: fadeOut 1.5s ease-in both; }}.dragon-playing { opacity: 0; &.is-animationed{ animation: fadeIn 1s ease both; }}
アニメーションはキーフレームを通じて生成されます:
// 淡出@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; }}// 淡入@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; }}
在这个过程仅通过CSS我们还有点难度的,需要通过JavaScript来触发,至于怎么触,后面的JavaScript部分来介绍。
其实难度在第二场景,因为在这个场景中我们涉及到三个部分的动画。我们来先看最难的一部分吧,就是龙。
前面也说过了,龙就要是分为五段,每段我们是通过CSS Sprites配合 steps() 完成。那么在这个过程需要将龙的每一部分拼合出来,如下图所示:
至于样式如下:
.dragon { position: absolute; height: 2.453333rem; //184px top: 0;}.dragon1{ width: 2.373333rem; //178px height: 2.506667rem; //188px left: 0; z-index: 5; background: url('//gw.alicdn.com/mt/TB16t_sIFXXXXaXapXXXXXXXXXX-1780-188.png') no-repeat; background-size: 23.733333rem 2.506667rem; //1780px 188px}
动画的 keyframes :
@keyframes dragon-1 { to { background-position: -23.733333rem; //1780px }}
触发动画:
.dragon-playing { opacity: 0; &.is-animationed{ animation: fadeIn 1s ease both; .dragon{ animation-duration: 1s; animation-timing-function: steps(10); animation-iteration-count: infinite; } .dragon1{ animation-name: dragon-1; } }
其它几个部分就不做详细阐述。在做龙的时候碰到两个坑。
第一个坑就是设计师希望将龙和小人分开来,这样有利于龙的更换(就是随时更换龙的设计效果)。听起来很有吸引力,但在实际制作过程中,才发现龙和小人的配合是非常难以达到一致。最后只好又更换到让他们合成在一起。
第二个坑就是,CSS Sprites的拼合。刚开始将其按纵向拼合,通过更改 background-position-y 的值。但动画效果非常生硬,才更换成水平排列。在排列Sprites时还有一个细节,就是每个区域(帧)大小一致,不然在播放时候,龙会乱帧。
第二个效果就是云彩飘动,其实这个效果非常简单,就是通过 transform 的 translate3d() 更换他们的 X 轴位置:
@keyframes colud { 0%,40%,100% { transform: translate(0,0); //0 } 20%, 50%, 80% { transform: translate(0.266667rem,0); //20px } 60% { transform: translate(-0.266667rem,0); //20px } }
第三个动效果是鞭炮的播放。最开始使用的是鞭炮和礼花合在一起,同样通过Sprites来实现,再配合 translate3d 将整个鞭炮往 Y 拉。虽然效果出来了,但PD同学说太假了,这不是在放鞭炮,整个鞭炮是在往上拉。想想也是,对于有追求的同学来说,还是很有必要来修改的。而在修改这个效果其实比舞龙动效还难。
最后的思路是把鞭炮和礼花拆分出来,为了动效更生动,鞭炮同样使用Sprites:
这两个要配合在一起,而且每个部分都采用了多个动画。
在这个过程最难的,也可以说是坑吧有两个:
鞭炮的逐渐消失,在这个过程尝试了很多种方案,都未见效。使用 transform 的话就会回到当初的效果,如果修改 hieght 的话,鞭炮会一闪而过。最后在无意中尝试修改鞭炮的 max-height 。简单点说就是慢慢变为 0 :
@keyframes bianpao2 { from { max-height: 4.426667rem; //332px } to { max-height: 0; }}
当然这种方案的效果也并不完全完美,怎么看度部都有一种被截取的效果。
另外就是鞭炮和礼花的配合。初始采用移动,但时间无法达到配合。情急之下,就只对礼花做定位处理:
.firecrackers { width: 2.213333rem; //166px; height: 4.426667rem; //332px; background: url('//gw.alicdn.com/mt/TB1zoB3LpXXXXbCXXXXXXXXXXXX-332-332.png') no-repeat; background-size: 4.426667rem 4.426667rem; //332px 332px position: absolute; top: -0.213333rem; //16px &.firecrackers-left{ //left: 0.133333rem; // 10px left: 0; } &.firecrackers-right { //right: 0.133333rem; // 10px right: -0.533333rem; //40px } &:after { content: ""; width: 1.626667rem; //122px; height: 1.2rem; //90px; position: absolute; bottom: -0.706667rem; //-53px; left: 0.066667rem; //5px; background: url('data:image/png;base64,B...') no-repeat; background-size: 2.986667rem 1.2rem; //224px 90px; }}
居然看上去也还是能勉强接受。
最后还有一个效果需要特别提出来,就是龙的位置。因为手淘首页在龙的下面就已嵌入了一个进入年货节主会场的按钮(这个是Native同学配置的)。而我们要处理的是动画的层必须先遮盖住。
.dragon-wrap { width: 10rem; height: 2.986667rem; //224px background:url('//gw.alicdn.com/mt/TB17q71LXXXXXbWXpXXXXXXXXXX-750-224.png') no-repeat center; background-size: 10rem 2.986667rem; position: absolute; top: 5.2rem;//390px}
但坑来了,手淘在不同的终端设备中,顶部的距离都不一样。这下就烦了,在实在没办法的情况下,只做了手淘的iOS设备做了处理:
@media only screen and (min-device-width : 320px) and (max-device-width : 480px) { .dragon-wrap { top: 5.2rem;//390px }}// iphone5 & 5s@media only screen and (min-device-width : 320px) and (max-device-width : 568px) { .dragon-wrap { top: 5.2rem;//390px }}// iphone6@media only screen and (min-device-width : 375px) and (max-device-width : 667px) { .dragon-wrap { top: 4.8rem; //360px }}// iphone6 +@media only screen and (min-device-width : 414px) and (max-device-width : 736px) { .dragon-wrap { top: 4.666667rem; //350px }}
在手猫中还是会有一点遮住手焦。在安卓设备下就更会错位严重了。到目前为止没找到更好的解决方案。
样式效果已处理完成。但整个动画我们还是需要JavaScript来触发。而且还有一些其他需要处理的。比如说时间的设置、音乐的控制等。
JavaScript做了以下几件事情:
// 控制音乐的播放function musicPlayer (){ var dragonStage = document.getElementById('dragon-poplayer'), switcher = document.getElementById('music'), media = switcher.getElementsByTagName('audio')[0], chooseMusic = document.getElementById('music-control'), wantedDragonDance = document.getElementById('dragon-ready-play'), dragonDanceStar = document.getElementById('dragon-playing'), firecrackers = document.querySelector('.firecrackers'); // 获取舞龙音乐选中开始时间 var musicStartTime = pageData['startTime']; // 获取舞龙音乐选中结束时间 var musicStopTime = pageData['endTime']; // 将设置的时间字符串(按冒号)拆分为两部分 var timeStart = musicStartTime.split(':'); var timeEnd = musicStopTime.split(':'); // 设置限制的开始时间 var limitStart = new Date(); limitStart.setHours(timeStart[0]); limitStart.setMinutes(timeStart[1]); // 设置限制的结束时间 var limitEnd = new Date(); limitEnd.setHours(timeEnd[0]); limitEnd.setMinutes(timeEnd[1]); // 获取系统当前时间 var nowTime = new Date(); // 如果系统时间在 限制时间之间,checkbox不选中,否则自动选中 chooseMusic.checked = nowTime < limitStart || nowTime > limitEnd; switcher.addEventListener ('click', function (){ var currentStatus = media.paused ? 'pause' : 'play'; var wantedStatus = currentStatus === 'pause' && !chooseMusic.checked ? 'play' : 'pause'; media[wantedStatus](); // 如果wantedDragonDance 没有is-animationed类名,就添加,反之什么也不做 if(!wantedDragonDance.classList.contains('is-animationed')){ wantedDragonDance.classList.add('is-animationed'); } }, false); // 监听wantedDragonDance的webkitAnimationEnd // 如果wantedDragonDance的动画完成,给dragonDanceStar 添加类名is-animationed wantedDragonDance.addEventListener('webkitAnimationEnd', function(){ dragonDanceStar.classList.add('is-animationed'); }); //监听鞭炮的动作,如果动画播放完,音乐停止,并且删除整个舞台和关闭Poplayer firecrackers.addEventListener('webkitAnimationEnd', function(e){ media.pause(); document.body.removeChild(dragonStage); window.WindVane.call('WVPopLayer', 'close', {}); }, false); }
// 禁止滑动function cancleDocumentScroll () { document.addEventListener('touchmove', function (e) { e.preventDefault(); return false; }, false);}
// 关闭WVPopLayer 和 音乐function closeAll () { var colseBtn = document.getElementById('close'), switcher = document.getElementById('music'), media = switcher.getElementsByTagName('audio')[0]; colseBtn.addEventListener('click', function () { window.WindVane.call('WVPopLayer', 'close', {}); media.pause(); var source = appname === 'TM' ? 2 :1 ; goldlog('/nhj.1.4','','from='+ source,'H1703624'); }, false);}
function init (){ window.WindVane.call('WVPopLayer', 'display', {}); window.WindVane.call('WVPopLayer', 'increaseReadTimes', {}, function(s){ // do something when success; }, function(e) { // do something when failed; }); musicPlayer (); cancleDocumentScroll (); closeAll ();}// 开始执行函数document.addEventListener('DOMContentLoaded', init, false);
虽然我们整个动画是使用CSS和JavaScript完成的,也可以说是一个Web Animation。那么要放到APP中,还是需要特殊处理的。在这里我们使用了一种技术: POPLAYER 。
有关于POPLAYER相关的介绍可以阅读《 POPLAYER起来HIGH~~ 》一文。如果你无法理解,就简单的把他当作是一个WebView或者是一个 iframe 吧。至于怎么做POPLAYER,偶也不懂。
内容がかなり長いので、読んで少し疲れましたか?記事全体では、お披露目アニメーションの制作過程を中心に紹介しています。簡単に言うと、Webアニメーションを通じてgifアニメーションをWebアニメーションに変換する方法です。制作プロセス全体は主に CSS のアニメーション プロパティを使用し、CSS スプライトと連携します。もちろん、この効果にもいくつかの欠陥があり、特に POPLAYER では、デバイスの 3D アクセラレータを一時的にオンにすることができず、アプリのパフォーマンスは依然として制限されています。また、パフォーマンスが低い一部のデバイスでは、それがより顕著になります。この点については、今後の技術の蓄積でさらに改善できることを期待しています。
通称「Da Mo」、W3CPlus の創設者、現在は淘宝網で働いています。中国の Drupal コミュニティの中心メンバーの 1 人。彼は HTML5、CSS3、Sass などのフロントエンド スクリプト言語について非常に深い理解と豊富な実践経験を持っており、特に CSS3 の研究に注力しており、中国で最初に研究と使用を行った人物の 1 人です。 CSS3テクノロジー。 CSS3、Sass、Drupalの中国人エバンジェリスト。 2014 年に『図解 CSS3: コア技術と事例実践』を出版しました。