انیمیشن های DOM در جاوا اسکریپت

23 فروردین 1398
Advanced-Javascript-html-dom-animation

با سلام و احترام خدمت شما همراهان گرامی روکسو، در این قسمت از آموزش جاوا اسکریپت میخواهیم با موضوع انیمیشن ها در جاوا اسکریپت آشنا شویم. مبحث انیمیشن ها در جاوا اسکریپت بسیار پیچیده است و نیاز به یک دوره ی جداگانه دارد اما در این قسمت با مفاهیم اصلی و نحوه ی کار انیمیشن های DOM در جاوا اسکریپت آشنا می شویم.

ممکن است بپرسید منظور ما از انیمیشن های DOM چیست؟ منظور ما این است که در دنیای وب راه های متفاوتی برای ساخت انیمیشن و پویا نمایی صفحات وجود دارد اما ما میخواهیم راهی بسیار ساده و ابتدایی در این مورد را بررسی کنیم که با دستکاری DOM انجام میگیرد.

انیمیشن از طریق DOM

بهترین روش یادگیری انیمیشن ها از طریق مثال است بنابراین بیایید ابتدا یک صفحه ی ساده ی وب را به وجود بیاوریم:

<!DOCTYPE html>
<html>
<body>

<h1>My First JavaScript Animation</h1>

<div id="animation">My animation will go here</div>

</body>
</html>

حالا باید فضایی را تعریف کنیم که انیمیشن ما در آن انجام شود:

<div id ="container">
  <div id ="animate">My animation will go here</div>
</div>

در این کد که قرار است به صفحه ی اصلی ما اضافه شود دو عدد عنصر <div> را به صورت تو در تو (nested) قرار داده ایم اما میدانیم که div ها تنها container (نگه دارنده ی) دیگر عناصر هستند و چیزی درونشان نیست بنابراین بیایید به این دو عنصر استایل بدهیم. برای div اول (فضایی که در آن انیمیشن اتفاق می افتد) ;position: relative و برای div دوم (که خود انیمیشن خواهد بود ;position: absolute قرار می دهیم. سپس بقیه ی استایل ها را طبق سلیقه ی خودمان اضافه می کنیم.

کد من به این شکل است:

<!Doctype html>
<html>
<style>
#container {
  width: 400px;
  height: 400px;
  position: relative;
  background: yellow;
}
#animate {
  width: 50px;
  height: 50px;
  position: absolute;
  background: red;
}
</style>
<body>

<h2>My First JavaScript Animation</h2>

<div id="container">
<div id="animate"></div>
</div>

</body>
</html>

مشاهده ی خروجی در JSBin

اگر به خروجی نگاهی بیندازید متوجه می شوید که ما div اول را تبدیل به یک مربع زرد رنگ کرده ایم و div دوم را به صورت یک مربع کوچکتر و به رنگ قرمز درون آن گذاشته ایم. حالا میخواهیم کاری کنیم که مربع قرمز رنگ درون مربع زرد رنگ جا به جا شود که همان انیمیشن ما خواهد بود. اینجاست که جاوا اسکریپت وارد کار می شود:

function myMove() {
  var elem = document.getElementById("animate"); 
  var pos = 0;
  var id = setInterval(frame, 5);
  function frame() {
    if (pos == 350) {
      clearInterval(id);
    } else {
      pos++; 
      elem.style.top = pos + 'px'; 
      elem.style.left = pos + 'px'; 
    }
  }
}

(کد جاوا اسکریپت برای انیمیشن)

به نظر شما این کد جاوا اسکریپت که قرار است بعدا به کد اصلی ما اضافه شود چه کار می کند؟

همانطور که میبینید ابتدا یک تابع تعریف کرده ایم و نام آن را myMove (به معنی حرکت من) گذاشته ایم. سپس یک متغیر به نام elem (مخفف element و به معنای «عنصر») ساخته ایم. از اسمش مشخص است که چه چیزی را در این متغیر قرار داده ایم:

 var elem = document.getElementById("animate");

در قسمت های قبلی توضیح دادیم که اگر می خواهیم در جاوا اسکریپت دستوری را بنویسیم که عنصری از DOM را بگیرد باید با document شروع کنیم چرا که شیء اصلی در مرورگر ما می باشد. سپس از دستور getElementById استفاده کرده ایم که اگر یادتان باشد کارش دریافت عناصر HTML بر اساس id آن ها بود؛ animate را به آن داده ایم.

سپس یک متغیر دیگر به اسم pos (مخفف position و به معنی «موقعیت مکانی») ساخته ایم و فعلا مقدارش را صفر گذاشته ایم. متغیر سوم نیز id نام دارد و با استفاده از تابع setInterval به آن گفته ایم که هر 5 میلی ثانیه تابع frame را اجرا کند. در مورد تابع setInterval در دوره ی گذشته صحبت کرده ایم ولی برای یادآوری دوباره مثالی از آن می زنم.

تابع ()setInterval دو آرگومان میگیرد که اولی یک تابع است و دومی زمان (در واحد میلی ثانیه). کار این تابع این است که تابع مورد نظر ما را هر فلان میلی ثانیه تکرار می کند.

مثال:

<!DOCTYPE html>
<html>
<body>

<p dir='rtl'>هنگامی که روی دکمه ی زیر کلیک کنید، پیام "سلام به شما" هر 3 ثانیه یک بار برایتان نمایش داده می شود.</p>

<button onclick="myFunction()">Try it</button>

<script>
var myVar;

function myFunction() {
  myVar = setInterval(alertFunc, 3000);
}

function alertFunc() {
  alert("سلام به شما");
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

در مثال بالا یک دکمه دارید. اگر روی این دکمه کلیک کنید، هر 3 ثانیه یک بار، پیام «سلام به شما» برایتان به نمایش در می آید. کار با این تابع آسان است و مطمئن هستم منظور من را فهمیده اید اما بیایید کمی پیشرفته تر شویم!

یکی دیگر از توابع مربوط به Interval می باشد تابع ()clearInterval است که تابع ()setInterval را نگه می دارد و اجازه نمی دهد دیگر اجرا شود. در مثال زیر کدها را طوری نوشته ایم که هر ثانیه ساعت ما آپدیت شود (با استفاده از setInterval) بنابراین یک ساعت دیجیتال را داریم.

سپس دکمه ای قرار داده ایم که اگر آن را بزنید، دیگر setInterval اجرا نمی شود (این همان clearInterval است):

<!DOCTYPE html>
<html>
<body>

<p>A script on this page starts this clock:</p>

<p id="demo"></p>

<button onclick="myStopFunction()">Stop time</button>

<script>
var myVar = setInterval(myTimer, 1000);

function myTimer() {
  var d = new Date();
  var t = d.toLocaleTimeString();
  document.getElementById("demo").innerHTML = t;
}

function myStopFunction() {
  clearInterval(myVar);
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

حالا که با توابع ()clearInterval و ()setInterval آشنا شدیم بهتر است برویم سراغ مبحث اصلی خودمان!

ما در (کد جاوا اسکریپت برای انیمیشن) به تابع ()setInterval گفته ایم که هر 5 میلی ثانیه تابع ()frame را اجرا کند. سپس تابع فریم را تعریف کرده ایم.

سوال: چرا تابع frame را ابتدا استفاده و سپس تعریف کرده ایم؟ قبل از اینکه تابعی تعریف شود چطور می توان از آن استفاده کرد؟

پاسخ: در بسیاری از زبان های برنامه نویسی چنین کاری خطا محسوب می شود اما اگر یادتان باشد گفته بودیم که در جاوا اسکریپت مبحثی به نام Hoisting وجود دارد. Hoisting باعث می شود این کد صحیح باشد.

در تابع ()frame گفته ایم اگر pos == 350 یعنی موقعیت افقی و عمودی عنصر به 350 پیکسل رسید clearInterval کند تا دیگر جلو نرود. به نظرتان چطور به عدد 350 رسیدیم؟ اگر یادتان باشد div اول که فضای انیمیشن بود دارای طول و عرض 400 پیکسلی بود و div دوم نیز طول و عرض 50 پیکسلی داشت. اگر بخواهیم div دوم در div اول باقی بماند و از آن خارج نشود باید 400 را منهای 50 کنیم که می شود 350!

سپس گفته ایم اگر pos == 350 نبود، یک واحد به pos اضافه کن (++pos) و بعد از آن دستور زیر را آورده ایم:

elem.style.top = pos + 'px';
elem.style.left = pos + 'px';

این دو دستور در واقع مقدار CSS عنصر را تغییر می دهند. style.top یعنی کد، به استایل های CSS برو فاصله از بالای این عنصر را برابر pos پیکسل قرار بده! این کار را برای فاصله از چپ نیز انجام داده ایم. از طرفی می دانیم تابع frame هر 5 میلی ثانیه توسط setInterval اجرا می شود که یعنی هر 5 میلی ثانیه یک واحد به pos اضافه شده و طبعا فاصله از بالا و چپ یک پیکسل اضافه می شود. این مسئله تا زمانی ادامه پیدا می کند که pos برابر 350 پیکسل شود و سپس clearInterval اجرا شود.

حالا مثال نهایی را می بینیم:

<!DOCTYPE html>
<html>
<style>
#container {
  width: 400px;
  height: 400px;
  position: relative;
  background: yellow;
}
#animate {
  width: 50px;
  height: 50px;
  position: absolute;
  background-color: red;
}
</style>
<body>

<p><button onclick="myMove()">Click Me</button></p> 

<div id ="container">
  <div id ="animate"></div>
</div>

<script>
function myMove() {
  var elem = document.getElementById("animate");   
  var pos = 0;
  var id = setInterval(frame, 5);
  function frame() {
    if (pos == 350) {
      clearInterval(id);
    } else {
      pos++; 
      elem.style.top = pos + "px"; 
      elem.style.left = pos + "px"; 
    }
  }
}
</script>

</body>
</html>

مشاهده ی خروجی در JSBin

این مقاله پایه ی بحث انیمیشن ها را به شما نشان می دهد و بسیار ساده است. شما می توانید انیمیشن ها را بسیار پیشرفته تر و حتی بعضا تنها با CSS و بدون نیاز به جاوا اسکریپت پیاده کنید. چند مثال پیشرفته تر برایتان آورده ام!

انیمیشن Loading با جاوا اسکریپت

ابتدا کدهای CSS:

.preloader {
  height: 100%;
  width: 100%;
  background: #fff;
  position: fixed;
  left: 0;
  top: 0;
  z-index: 10000;
  perspective: 1600px;
  perspective-origin: 20% 50%;
  transition: 0.5s all;
  opacity: 1;
}

.spinner {
  width: 80px;
  height: 80px;
  border: 2px solid #f3f3f3;
  border-top: 3px solid #0088cf;
  border-radius: 100%;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  animation: spin 1s infinite linear;
}

.preloader.fade {
  opacity: 0;
}

.b-ico-preloader {
  background: url(http://weblaboratory.in.ua/wp-content/themes/graphy/images/new_logo.svg);
  background-size: cover;
  width: 52px;
  height: 67px;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  margin: auto;
  animation: ico 5s infinite linear;
  transform-style: preserve-3d;
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@keyframes ico {
  from {
    transform: rotateY(0deg);
  }
  to {
    transform: rotateY(360deg);
  }
}

سپس کدهای جاوا اسکریپت:

var preload = document.createElement('div');

preload.className = "preloader";
preload.innerHTML = '<div class="b-ico-preloader"></div><div class="spinner"></div>';
document.body.appendChild(preload);

window.addEventListener('load', function() {
  // Uncomment to fade preloader after document load
  // preload.className +=  ' fade';
  // setTimeout(function(){
  //    preload.style.display = 'none';
  // },600);
})

مشاهده ی خروجی در JSBin

انیمیشن متنی تنها با CSS

کدهای HTML:

<div id=container>
  Make 
  <div id=flip>
    <div><div>wOrK</div></div>
    <div><div>lifeStyle</div></div>
    <div><div>Everything</div></div>
  </div>
  AweSoMe!
</div>

<p>a css3 animation demo</p>

کدهای CSS:

@import url('https://fonts.googleapis.com/css?family=Roboto:700');

body {
  margin:0px;
  font-family:'Roboto';
  text-align:center;
}

#container {
  color:#999;
  text-transform: uppercase;
  font-size:36px;
  font-weight:bold;
  padding-top:200px;  
  position:fixed;
  width:100%;
  bottom:45%;
  display:block;
}

#flip {
  height:50px;
  overflow:hidden;
}

#flip > div > div {
  color:#fff;
  padding:4px 12px;
  height:45px;
  margin-bottom:45px;
  display:inline-block;
}

#flip div:first-child {
  animation: show 5s linear infinite;
}

#flip div div {
  background:#42c58a;
}
#flip div:first-child div {
  background:#4ec7f3;
}
#flip div:last-child div {
  background:#DC143C;
}

@keyframes show {
  0% {margin-top:-270px;}
  5% {margin-top:-180px;}
  33% {margin-top:-180px;}
  38% {margin-top:-90px;}
  66% {margin-top:-90px;}
  71% {margin-top:0px;}
  99.99% {margin-top:0px;}
  100% {margin-top:-270px;}
}

p {
  position:fixed;
  width:100%;
  bottom:30px;
  font-size:12px;
  color:#999;
  margin-top:200px;
}

مشاهده ی خروجی در CodePen

روش های بسیاری برای انیمیشن سازی در دنیای وب وجود دارد و امیدوارم این قسمت شما را به این دنیای زیبا و جذاب معرفی کرده باشد. امیدوارم از این قسمت لذت برده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری جاوا اسکریپت پیشرفته توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.