ساخت ساعت با جاوا اسکریپت

?How to Build A Clock with JavaScript

How-to-Build-A-Clock-with-JavaScript

از آن جایی که تصاویر SVG را می توان با HTML نمایش داد، می توانیم آن ها را با جاوا اسکریپت دستکاری کنیم. این به این معنی است که می‌توانیم بخش‌هایی از تصویر را از با استفاده از کد متحرک کنیم، آن را هم کنشی کنیم، یا چیزهایی را در تصویر تغییر دهیم و از داده‌ها تصویر گرافیکی تولید کنیم.

در این مقاله قصد داریم یک ساعت بسازیم. از SVG برای رنگ آمیزی ساعت و از جاوا اسکریپت برای متحرک سازی عقربه ها استفاده خواهیم کرد. این آموزش تاحدودی پیشرفته‌ است، زیرا در آن با برخی از ویژگی‌های SVG و ساخت انیمیشن با جاوا اسکریپت آشنا می‌شویم. اگر می خواهید دید کلی تری از SVG ها داشته باشید، مقاله را بخوانید.

SVG در HTML

می دانیم که تصاویر SVG را می توان در HTML قرار داد. تگ SVG می تواند اندازه تصویر و محل قرارگیری عناصر تصویر را مشخص کند. عناصر تصویر با توجه به موقعیت خود در تصویر قرار می گیرند. ViewBox مشخص می کند که این موقعیت ها (position ها) چگونه باید تعریف شوند. دو عدد اول در تگ svg ویژگی موقعیت را در گوشه بالا سمت چپ تنظیم می کند. این دو عدد همراه با اندازه تعریف شده توسط دو عدد آخر تگ svg، یک سیستم مختصات را تشکیل می دهند.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="index.css">
    <title>Watch</title>
</head>
<body>
    <svg width="200" height="200" viewBox="-100 -100 200 200">
        <circle class="minute_marker" r="90" pathLength="60" />
        <circle class="hour_marker" r="90" pathLength="60" />
        <text id="text" class="text" x="45" y="5"></text>
      
        <g id="hour_hand">
          <line class="hand" x1="0" y1="0" x2="0" y2="-50" />
          <line class="hand hand--thick" x1="0" y1="-12" x2="0" y2="-50" />
        </g>
      
        <g id="minute_hand">
          <line class="hand" x1="0" y1="0" x2="0" y2="-80" />
          <line class="hand hand--thick" x1="0" y1="-12" x2="0" y2="-80" />
        </g>
      
        <g id="second_hand">
          <line class="hand hand--second" x1="0" y1="12" x2="0" y2="-80" />
        </g>
      
        <circle class="center" r="3" />
      </svg>
      <script src="index.js"></script>
</body>
</html>

در این مثال، سیستم مختصات را در مرکز قرار می دهیم. مختصات 0,0 در وسط تصویر قرار دارد. ما یک viewBox تنظیم کردیم که گوشه سمت چپ بالا باید مختصات -100،-100 و طول و عرض هر دو 200 واحد باشد. در این مثال، اندازه تعریف شده توسط طول و عرض، و اندازه تعریف شده توسط viewBox یکسان است. این به این معنی است که یک واحد در تصویر یک پیکسل در مرورگر خواهد بود. این همیشه موضوع درست نیست. اگر این دو با هم مطابقت ندارند، تصویر بزرگ یا کوچک می شود.

چگونه عقربه دقیقه شمار و ساعت شمار را بسازیم؟

اکنون اجازه دهید شروع به کدنویسی ساعت خود کنیم. با عقربه دقیقه شمار و ساعت شمار آغاز می کنیم. راه های زیادی برای کشیدن این خطوط کوچک وجود دارد. می‌توانیم هر خطی را یکی یکی رسم کنیم، اما بهترین راه برای کشیدن آن، کشیدن یک دایره با property (ویژگی) dashed است. تگ circle در مثال ما دارای موقعیت مرکزی، شعاع، رنگ برای پر کردن تمام دایره و رنگ برای border آن و عرض border است. عناصر SVG اغلب دارای گزینه هایی برای style دهی مشابه با عناصر HTML هستند. اما این گزینه ها دارای نام های مختلفی هستند. می توانید ویژگی fill را به عنوان رنگ پس زمینه در CSS در نظر بگیرید.

ویژگی های stroke و stroke-width نیز همانند ویژگی های border-color و border-width هستند. فقط به خاطر داشته باشید که آن ها دقیقا یکسان نیستند. هم چنین از ویژگی fill برای تنظیم رنگ متن و از ویژگی stroke برای تنظیم رنگ یک خط استفاده می کنیم. اکنون چگونه در دایره خود نشانگرهای دقیقه ای را رسم کنیم؟ ممکن است با ویژگی border-style در CSS آشنا باشید. معمولا از یک border از نوع  solid استفاده می‌کنیم، ولی می‌توان از border از نوع  dashed نیز استفاده کرد. این style دهی های border چندان رایج نیستند، زیرا گزینه های زیادی برای تنظیم دقیق آنها در CSS نداریم.

در SVG نیز برای Style دهی امکانات مشابهی داریم. می‌توانیم در svg از ویژگی‌های stroke-dasharray، stroke-dashoffset و pathLength استفاده کنیم.به عنوان نمونه، اگر عددی را به عنوان stroke-dasharray قرار دادیم. این کار باعث ایجاد یک border از نوع dashed می شود که در آن پاره خط و شکاف هر دو دارای طول یکسان هستند.

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

می توانیم ویژگی pathLength را تنظیم کنیم. استفاده از این ویژگی کمی مشکل است. اندازه دایره را تغییر نمی‌دهد، اما بر نحوه تعریف ویژگی dasharray تاثیر می‌گذارد. خط تیره ها به گونه ای رسم می شوند که گویی دایره دارای محیطی است که با طول مسیر تعریف شده است.بنابراین اجازه دهید طول مسیر را روی 60 تنظیم کنیم که نشان دهنده 60 دقیقه است. اکنون مجموع پاره خط و شکاف باید در مجموع 1 باشد. من در این مثال آن را روی 0.2 و 0.8 قرار دادم.

کار تقریبا تمام شده است، اما هنوز یک کار کوچک انجام نشده است. خط چین یاdashing از موقعیت اشتباه شروع شده است. برای رفع آن، باید با استفاده از ویژگی stroke-dashoffset، آن را به نصف طول قطعه خط تغییر دهیم. خاصیت offset خط تیره می‌تواند کمی غیرمعمول باشد، زیرا یک مقدار مثبت در این جا باعث تغییر حرکت به عقب می‌شود. همچنین می توانید آن را روی یک عدد مثبت تنظیم کنید تا آن را به جلو منتقل کنید.

به همین ترتیب، می توانیم نشانگر ساعت را تنظیم کنیم. یک تگ circle جدید با ویژگی های تقریبا یکسان اضافه می کنیم. تنها چیزی که متفاوت است رنگ و شکاف های طولانی تری است که در آرایه dash داریم.

. . .

<svg width="200" height="200" viewBox="-100 -100 200 200">
  <circle
    cx="0"
    cy="0"
    r="90"
    fill="transparent"
    stroke="#0f0e0e"
    stroke-width="7"
    stroke-dasharray="0.2 0.8"
    stroke-dashoffset="0.1"
    pathLength="60"
  />

  <circle
    cx="0"
    cy="0"
    r="90"
    fill="transparent"
    stroke="#f0f0c9"
    stroke-width="7"
    stroke-dasharray="0.2 4.8"
    stroke-dashoffset="0.1"
    pathLength="60"
  />
</svg>

. . .

در این جا ذکر این نکته مهم است که لایه بندی در SVG اهمیت دارد. برچسب‌هایی که در آینده به document اضافه می‌شوند، بالای برچسب‌های قبلی خواهند بود. اگر این دو دایره را به ترتیب مخالف اضافه کنیم، دقیقه شمار به طور کامل نشانگر ساعت را پوشش می دهد. از آن جایی که SVG اکنون در HTML قرار دارد، می توانیم برخی از ویژگی های آن را به CSS منتقل کنیم. با این حال، نمی توانیم همه ویژگی ها را جابه جا کنیم. بین ویژگی هایی که style را تعریف می کنند و ویژگی هایی که شکل یک عنصر را تعیین می کنند تفاوت وجود دارد. به عنوان نمونه، شعاع شکل دایره را مشخص می کند، بنابراین باید در کد SVG باقی بماند. از طرف دیگر می توانیم ویژگی های fill و stroke را جابه جا کنیم.

    <svg width="200" height="200" viewBox="-100 -100 200 200">
      <circle class="minute_marker" r="90" pathLength="60" />
      <circle class="hour_marker" r="90" pathLength="60" />
    </svg>
    

.hour_marker {
  fill: transparent;
  stroke: #f0f0c9;
  stroke-width: 7;
  stroke-dasharray: 0.2, 4.8;
  stroke-dashoffset: 0.1;
}

.minute_marker {
  fill: transparent;
  stroke: #0f0e0e;
  stroke-width: 7;
  stroke-dasharray: 0.2, 0.8;
  stroke-dashoffset: 0.1;
}

کشیدن عقربه های ساعت

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

برای کشیدن عقربه ها از تگ line استفاده می کنیم. برای تعریف یک تگ line، باید مختصات شروع و پایان را به همراه رنگ stroke و ویژگی stroke-width تنظیم کنیم.برای این که همه چیز کمی زیباتر شود، می‌توانیم ویژگی stroke-linecap را نیز اضافه کنیم تا خط گرد داشته باشیم. این ویژگی های را با CSS اضافه می کنیم.

چگونه عقربه های ساعت را در جهت درست قرار دهیم؟

حال چگونه این خطوط را در موقعیت خود قرار دهیم؟ اگر یک id به المنت اختصاص دهیم، می‌توانیم به آن دسترسی داشته باشیم و آن را با جاوا اسکریپت دستکاری کنیم.با این حال، به کدام عنصر باید id بدهیم؟ ما دو المنت برای یک عقربه داریم. برای حل این مشکل می توانیم این دو المنت line را در یک تگ g گروه بندی کنیم. می توان تگ g را به عنوان تگ div در HTML در نظر گرفت. می‌توانیم یک id به این تگ g اختصاص دهیم، سپس می‌توانیم کل گروه را با جاوا اسکریپت بچرخانیم و در موقعیت مناسب قرار بدهیم.در فایل جاوا اسکریپت ابتدا عقربه ها را با استفاده از id آن ها به دست می آریم. سپس یک شی Date ایجاد می کنیم و ساعت، دقیقه و ثانیه فعلی را بدست می آوریم. و در نهایت، ویژگی transform عناصر را بر اساس این مقادیر تنظیم می کنیم.

const hoursElement = document.getElementById("hour_hand");
const minutesElement = document.getElementById("minute_hand");
const secondsElement = document.getElementById("second_hand");

const date = new Date();

const hour = date.getHours();
const minute = date.getMinutes();
const second = date.getSeconds();

hoursElement.setAttribute("transform", `rotate(${(360 / 12) * hour})`);
minutesElement.setAttribute("transform", `rotate(${(360 / 60) * minute})`);
secondsElement.setAttribute("transform", `rotate(${(360 / 60) * second})`);

ویژگی transform می‌تواند شامل تبدیل‌های متعددی مانند scale، translate یا skew باشد. برای تبدیل چرخشی یا rotate  به یک عدد نیاز دارد. این عدد یک چرخش بین 0 تا 360 درجه است. برای عقربه ساعت، 360 را بر 12 تقسیم می کنیم تا میزان چرخش در هر ساعت نیاز را به دست آوریم. سپس آن را در ساعت جاری ضرب می کنیم. این کار باید عقربه ساعت شمار را در ساعت فعلی قرار بدهد. برای دقیقه شمار و عقربه شمار هم همین کار را می کنیم با این تفاوت که 360 را بر 60 تقسیم می کنیم، زیرا یک ساعت 60 دقیقه و 1 دقیقه 60 ثانیه است. خوشبختانه برای ما، مرکز تبدیل به طور پیش فرض مبدا، یعنی مختصات 0,0 است.

 حرکت دادن عقربه های ساعت

تا این جای کار، ساعت زمان فعلی را نشان دهد، اما تصویر ثابت است و عقربه ها حرکت نمی کنند. برای همگام شدن با زمان و جلو رفتن ساعت از تابع requestAnimationFrame برای حرکت دادن عقربه ها استفاده کنیم.

const hoursElement = document.getElementById("hour_hand");
const minutesElement = document.getElementById("minute_hand");
const secondsElement = document.getElementById("second_hand");

function animate() {
  const date = new Date();

  const hour = date.getHours() % 12;
  const minute = date.getMinutes();
  const second = date.getSeconds();

  hoursElement.setAttribute("transform", `rotate(${(360 / 12) * hour})`);
  minutesElement.setAttribute("transform", `rotate(${(360 / 60) * minute})`);
  secondsElement.setAttribute("transform", `rotate(${(360 / 60) * second})`);

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

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

function animate() {
  const date = new Date();

  const hour = date.getHours() + date.getMinutes() / 60;
  const minute = date.getMinutes() + date.getSeconds() / 60;
  const second = date.getSeconds() + date.getMilliseconds() / 1000;

  hoursElement.setAttribute("transform", `rotate(${(360 / 12) * hour})`);
  minutesElement.setAttribute("transform", `rotate(${(360 / 60) * minute})`);
  secondsElement.setAttribute("transform", `rotate(${(360 / 60) * second})`);

  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

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

ساخت ساعت با جاوا اسکریپت

نویسنده شوید
دیدگاه‌های شما (2 دیدگاه)

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

علی
21 دی 1400
سلام لطفا سورس کد را هم قرار دهید.

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

محمدرضا قاضیان
20 دی 1400
ای کاش سورس کد رو هم قرار داده بودید

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