API–های صوتی و ویدئویی در جاوا اسکریپت – راهنمای جامع


HTML5 عناصری برای جاسازی رسانههای مختلف در اسناد معرفی کرده است. عناصر <video> و <audio> به همراه API-های خاص خود برای کنترل بازپخش رسانه و موارد دیگر به کار میروند. در این مقاله به بررسی شیوه انجام وظایف رایجی مانند ایجاد کنترلهای سفارشی برای بازپخش رسانه با استفاده از API-های صوتی و ویدئویی میپردازیم.
پیشنیاز مطالعه این مقاله آشنایی با مبانی جاوا اسکریپت و مقدمات API-های سمت کلاینت در جاوا اسکریپت است. هدف از مطالعه این مقاله نیز یادگیری شیوه استفاده از API مرورگر برای کنترل بازپخش ویدئو و صوت است. پیش از شروع لازم است ذکر کنیم برای مطالعه قسمت قبلی این مجموعه آموزشی میتوانید روی لینک زیر کلیک کنید:
صوت و ویدئوی HTML5
عناصر <video> و <audio> امکان جاسازی صوت و ویدئو را در صفحههای وب در اختیار ما قرار میدهند.
چنان که در یکی از بخشهای قبلی به صورت زیر دیدیم:
یک پیادهسازی معمول از این عناصر به صورت زیر است:
بدن ترتیب یک پخشکننده ویدئو درون مرورگر به صورت زیر ایجاد میشود:
شما میتوانید همه ویژگیهای HTML را در مقالهای که در بخش فوق لینک کردهایم ملاحظه کنید، اما با توجه به مقاصد این راهنما جالبترین خصوصیت controls است که امکان تعیین مجموعه کنترلهای بازپخش پیشفرض را در اختیار ما قرار میدهد. اگر این خصوصیت را تعیین نکرده باشید هیچ کنترل بازپخش ارائه نمیشود:
این کار فایده چندانی برای بازپخش ویدئو ندارد، اما با این حال مزیتهایی دارد. یک مشکل بزرگ در کنترلهای بومی مرورگر این است که در هر مرورگر متفاوت هستند و پشتیبانی بین مرورگری چندان مناسبی ندارند. مشکل بزرگ دیگر این است که کنترلهای بومی در اغلب مرورگرها از طریق کیبورد دسترسی مناسبی ندارند.
هر دو این مشکلها را میتوان با پنهان کردن کنترلهای بومی (با حذف خصوصیت controls) و برنامهنویسی کنترلهای سفارشی با استفاده از HTML، CSS و جاوا اسکریپت حل کرد. در بخش بعدی برخی ابزارهای مقدماتی برای نیل به این مقصود را مورد بررسی قرار میدهیم.
HTMLMediaElement API
API-ی HTMLMediaElement بخشی از استاندارد HTML5 است و ویژگیهایی ارائه میکند که با آنها میتوان پخشکنندههای صوت و ویدئو را به صورت برنامهنویسی شده کنترل کرد. برای نمونه شما میتوانید روی این API از متدهای مختلف از جمله ()HTMLMediaElement.play() ،HTMLMediaElement.pause و موارد دیگر استفاده کنید. این اینترفیس روی هر دو عنصر <audio> و <video> در دسترس است چون قابلیتهایی که میخواهید پیادهسازی کنید تقریباً یکسان هستند. در ادامه به بررسی یک مثال میپردازیم و قابلیتهایی را اضافه میکنیم.
مثال کامل شده ما به صورت زیر خواهد بود:
شروع
برای آغاز کردن این مثال ابتدا فایل فشرده زیر را دانلود کنید:
- media-player-start.zip (+)
این فایل را در یک دایرکتوری جدید روی هارد درایو خود از حالت فشرده خارج کنید. در این زمان اگر HTML را بارگذاری کنید، یک پخشکننده ویدئوی HTML کاملاً نرمال میبینید که با کنترلهای بومی رندر میشود.
بررسی HTML
فایل ایندکس HTML را باز کنید. چند قابلیت میبینید که به طور عمده شامل پخشکننده ویدئو و کنترلهای آن است:
- کل پخشکننده درون یک عنصر <div> پیچیده شده است و از این رو میتوانید در صورت نیاز همه آن را به صورت یک واحد مستقل استایلدهی کنید.
- عنصر <video> شامل دو عنصر <source> است به طوری که قالبهای مختلف را میتوان بسته به مرورگری که سایت را میبیند بارگذاری کرد.
- کنترلهای HTML احتمالاً جالبترین هستند:
- ما چهار <button> داریم که به ترتیب play/pause، stop، rewind و fast forward هستند.
- هر <button> یک نام class، یک خصوصیت data-icon برای تعریف آیکون یافته روی هر دکمه و یک خصوصیت aria-label بری ارائه توضیحی گویا در مورد هر دکمه دارد، چون برچسب قابل خواندن از سوی انسان درون تگها ارائه نکردهایم. زمانی که کاربران روی عناصری که شامل کنترلهای خصوصیت aria-label هستند فوکوس میکنند، از سوی نرمافزارهای قرائت صفحه خوانده میشوند.
- همچنین یک <div> تایمر وجود دارد که زمان گذشته از هنگامی که ویدئو پخش میشود را نشان میدهد. ما صرفاً به خاطر جالب بودن، دو مکانسیم گزارشدهی نیز ارائه میکنیم. یک <span> که شامل زمان گذشته به صورت دقیقه و ثانیه و یک <div> دیگر که از آن برای ایجاد نوار نشانگر افقی استفاده میشود که با گذشت زمان بسط مییابد.
بررسی CSS
اکنون فایل CSS را باز کنید تا آن را بررسی کنیم. CSS این مثال چندان پیچیده نیست، اما ما بخشهای جالبتر را توضیح میدهیم. قبل از هر چیز به استایلدهی controls. توجه کنید:
ما کار خود را با visibility کنترلهای سفارشی که روی hidden تنظیم شده آغاز میکنیم. در ادامه در بخش جاوا اسکریپت، کنترلها را روی مقدار visible تنظیم میکنیم. در ادامه خصوصیت controls را از عنصر <video> حذف میکنیم. بدن ترتیب در صورتی که جاوا اسکریپت به هر دلیلی بارگذاری نشود، کاربران میتوانند همچنان از ویدئو با کنترلهای بومی استفاده کنند.
در ادامه یک opacity به مقدار پیشفرض 0.5 برای کنترلها تنظیم میکنیم تا زمانی که کاربر میخواهد ویدئو را تماشا کند موجب حواسپرتی نشود. تنها زمانی که پخشکننده فوکوس بگیرد یا ماوس روی آن برود، کنترلها به صوت کاملاً مات دیده میشوند. دکمهها را درون نوار کنترل با استفاده از Flexbox طرحبندی کردیم تا همه چیز آسانتر شود.
در ادامه نگاهی به آیکونهای دکمهها داریم:
قبل از هر چیز در ابتدای CSS از بلوک font-face@ برای ایمپورت یک فونت وب سفارشی استفاده میکنیم. همه کاراکترهای الفبا معادل آیکونهای رایج هستند و میتوانید در یک اپلیکیشن از آنها استفاده کنید.
سپس از محتوای تولیدشده برای نمایش آیکون روی هر دکمه استفاده میکنیم:
- از سلکتور before:: برای نمایش محتوا پیش از هر عنصر <button> استفاده میکنیم.
- ما از مشخصه content برای تعیین محتوای نمایش یافته در هر مورد به صورت برابر با معادل خصوصیت data-icon استفاده میکنیم. در مورد دکمه play، این data-icon شامل حرف بزرگ P است.
- ما یک فونت وب سفارشی برای دکمهها با استفاده از font-family تعیین میکنیم. در این فونت، P در واقع آیکون play است و از این رو آیکون دکمه play رویان نمایش پیدا میکند.
فونتهای آیکون به دلایل مختلف جالب هستند. یکی از این دلایل آن است که میزان درخواستهای HTTP را کاهش میدهند، چون دیگر نیاز نیست که این آیکونها را به صورت فایلهای تصویر دانلود کنید، از سوی دیگر مقیاسپذیری بالایی دارند و این واقعیت وجود دارد که میتوان از مشخصههای متنی مانند color و text-shadow برای استایلدهی به آنها استفاده کرد.
در ادامه به بررسی CSS تایمر میپردازیم:
ما <timer <div. بیرونی را به صورت flex: 5 تنظیم میکنیم. بنابراین بخش عمده عرض نوار کنترلها را اشغال میکند. همچنین تنظیم دیگری به صورت position: relative داریم و بنابراین میتوانیم عناصر را درون آن به سادگی بر اساس کرانهایش و نه بر اساس مرزهای عنصر <body> تنظیم کنیم.
<div> درونی به صورت absolute طوری موقعیتیابی شده است که مستقیماً روی <div> بیرونی قرار بگیرد. همچنین دارای عرض اولیه 0 است و از این رو نمیتوان آن را کلاً مشاهده کرد. زمانی که ویدئو پخش میشود عرض آن با افزایش زمان سپریشده از پخش ویدئو، از طریق جاوا اسکریپت افزایش مییابد.
همچنین به عناصر <div> و <span> مقدار z-index صحیحی میدهیم تا تایمر در بالا دیده شود و <div> داخلی زیر آن نمایش یابد. بدین ترتیب مطمئن میشویم که میتوانیم همه اطلاعات را مشاهده کنیم و هیچ عنصری، عنصر دیگر را پنهان نمیکند.
پیادهسازی جاوا اسکریپت
بدین ترتیب ما تقریباً اینترفیس HTML و CSS خود را آماده داریم و اینک کافی است همه دکمهها را طوری تنظیم کنیم که کنترلها به درستی کار کنند. ابتدا یک فایل جاوا اسکریپت در همان سطح دایرکتوری فایل index.html میسازیم. آن را custom-player.js مینامیم.
روی این فایل کد زیر را درج میکنیم:
در این کد متغیرهایی برای نگهداری ارجاعهایی به همه اشیایی که میخواهیم دستکاری کنیم ایجاد میکنیم. ما سه متغیر داریم:
- عنصر <video> و نوار کنترل
- دکمههای play/pause ،stop ،rewind و fast forward
- عنصر <div> بیرونی که تایمر در آن قرار دارد، <span> خواندن تایمر دیجیتال و <div> درونی که با گذشت زمان عریضتر میشود.
سپس کد زیر را در انتهای کد قبلی وارد میکنیم:
این دو خط کنترلهای پیشفرض مرورگر را از ویدئو حذف میکنند و موجب میشوند که کنترلهای سفارشی پدیدار شوند.
پخش و ایجاد مکث در ویدئو
اینک مهمترین بخش کنترل یعنی دکمه پخش/مکث را پیادهسازی میکنیم.
قبل از هر چیز کد زیر را به انتهای کدهای قبلی اضافه کنید تا تابع ()playPauseMedia هنگامی که دکمه پخش کلیک میشود، فراخوانی شود:
اکنون ()playPauseMedia را تعریف کنید و کد زیر را بار دیگر به کدهای قبلی اضافه کنید:
در این کد ما از یک گزاره if استفاده میکنیم تا بررسی کنیم آیا ویدئو مکث یافته است یا نه. مشخصه HTMLMediaElement.paused در صورتی که رسانه مکث یابد مقدار true بازگشت میدهد. این مکث شامل همه زمانهایی است که به هر علتی ویدئو در حال پخش نباشد و از جمله شامل زمانی که در زمان 0 پس از بارگذاری قرار دارد میشود. اگر ویدئو مکث یافته باشد، مقدار خصوصیت data-icon را روی دکمه پخش به صورت u تعیین میکنیم که آیکون paused را نشان میدهد و متد ()HTMLMediaElement.play را برای پخش ویدئو فراخوانی میکنیم.
در کلیک دوم، دکمه دوباره به حالت قبل بازمیگردد و آیکون Play نمایش پیدا خواهد کرد و ویدئو با متد ()HTMLMediaElement.paused مکث مییابد.
متوقف کردن ویدئو
در ادامه کارکرد مدیریت توقف ویدئو را پیادهسازی میکنیم. پس از خطوط ()addEventListener زیر که قبلاً به کد افزودیم، کد زیر را اضافه کنید:
رویداد ()click بدیهی است و میخواهیم پخش ویدئو را با اجرای تابع ()stopMedia هنگامی که دکمه stop کلیک شد اجرا کنیم. با این حال علاوه بر آن میخواهیم هنگامی که پخش ویدئو پایان یافت نیز متوقف شود. این وضعیت با اجرای رویداد ended مشخص میشود و میخواهیم یک شنونده نیز برای اجرای تابعی در زمان صدور این رویداد داشته باشیم.
سپس ()stopMedia را تعریف میکنیم و به این منظور تابع زیر را در ادامه ()playPauseMedia اضافه میکنیم:
یک متد ()stop روی API-ی HTMLMediaElement وجود دارد که متناظر ()pause برای ویدئو است و مشخصه currentTime را به صورت 0 تنظیم میکند. تعیین currentTime روی یک مقدار (به ثانیه) بیدرنگ موجب پرش رسانه به آن موقعیت میشود.
تنها کاری که مانده تا انجام دهیم این است که آیکون نمایش یافته را به آیکون Play عوض کنیم. صرفنظر از این که ویدئو مکث یافته یا در حال پخش است زمانی که دکمه توقف زده شود، میخواهیم آماده پخش شود.
کشیدن ویدئو به جلو و عقب
روشهای زیادی وجود دارد که میتوان با آنها یک ویدئو را به سمت جلو یا عقب کشید. در این بخش یک روش نسبتاً پیچیده برای انجام این کار را توضیح میدهیم که حسنش این است وقتی دکمههای مختلف با ترتیب غیرمنتظره فشرده شوند، از کار نمیافتد.
قبل از هر چیز دو خط ()addEventListener را زیر خطوط قبلی اضافه کنید:
اکنون روی تابعهای دستگیره رویداد، کد زیر را در ادامه تابعهای قبلی برای تعریف ()mediaBackward و ()mediaForward اضافه کنید:
احتمالاً متوجه خواهید شد که ابتدا دو متغیر را به نامهای intervalFwd و intervalRwd مقداردهی کردهایم. کاربرد آنها را در ادامه بیشتر توضیح خواهیم داد.
اینک به توضیح ()mediaBackward میپردازیم که کارکردی مشابه ()mediaForward اما دقیقاً معکوس آن دارد.
ما هر کلاس و بازهای که روی کارکرد fast forward تعیینشده را پاک میکنیم. دلیل این کار آن است که اگر دکمه rwd را پس از دکمه fwd بزنیم، میخواهیم هر کارکرد fast forward که قبلاً وجود داشت حذف شود و کارکرد rewind جایگزین آن شود. اگر هر دو این کارکردها را با هم امتحان کنیم، پخشکننده از کار میافتد.
ما از یک گزاره if برای بررسی این نکته که کلاس active روی دکمه rwd تعیینشده یا نه استفاده میکنیم که نشان میدهد قبلاً فشرده شده است. classList یک مشخصه نسبتاً کارآمد است که روی هر عنصر وجود دارد و شامل لیستی از همه کلاسهایی است که روی یک عنصر تعیین شدهاند و متدهایی برای افزودن/حذف کلاسها و موارد دیگر را نیز شامل میشود. ما از متد ()classList.contains استفاده میکنیم تا بررسی کنیم آیا لیست شامل کلاس active است یا نه. این متد یک نتیجه بولی true/false بازگشت میدهد.
اگر active روی دکمه rwd تنظیم شده باشد، آن را با استفاده از ()classList.remove حذف میکنیم و بازهای که در زمان فشرده شدن اولیه دکمه تعیینشده را حذف میکنیم. در این مرحله از ()HTMLMediaElement.play برای لغو rewind و شروع پخش برنامهنویسی شده ویدئو استفاده میکنیم.
در ادامه اگر کلاس active هنوز روی دکمه rwd تعیین نشده باشد آن را با استفاده از ()classList.add تعیین میکنیم، ویدئو را با استفاده از ()HTMLMediaElement.pause مکث میکنیم و متغیر را برابر با فراخوانی ()setInterval قرار میدهیم. زمانی که ()setInterval را فراخوانی کنیم یک بازه فعال ایجاد میکند و این بدان معنی است که تابع را با ارائه آرگومان اول در هر x میلیثانیه اجرا میکند که x مقدار پارامتر دوم است. بنابراین در این مورد تابع ()windBackward هر 200 میلیثانیه یک بار اجرا شود. برای متوقف ساختن اجرای ()setInterval باید ()clearInterval را فراخوانی کنیم و یک نام مشخصکننده برای بازه تعیین کنیم. در این مورد نام متغیر intervalRwd است.
در نهایت باید تابعهای ()windBackward و ()windForward را تعریف میکنیم که در فراخوانیهای ()setInterval اجرا میشوند. کد زیر را در ادامه تابعهای قبلی اضافه کنید:
در این مورد نیز صرفاً به بررسی تابع اول میپردازیم، چون هر دو تقریباً یکسان اما در جهت عکس هستند. در ()windBackward کارهای زیر را انجام میدهیم. به خاطر بسپارید که وقتی بازه فعال است این تابع هر 200 میلیثانیه یک بار اجرا میشود.
ما کار خود را با یک گزاره if آغاز میکنیم و به بررسی این نکته میپردازیم که آیا زمان جاری کمتر از 3 ثانیه است یا نه. یعنی اگر ویدئو را 2 ثانیه دیگر به عقب بکشیم آیا از آغاز ویدئو جلوتر میرود یا نه. اگر این بررسی صورت نگیرد ویدئو رفتار عجیبی خواهد داشت و از این رو در صورتی که این شرایط وجود داشته باشد پخش ویدئو را با فراخوانی ()stopMedia متوقف میکنیم، کلید active را از دکمه rewind حذف میکنیم و بازه intervalRwd را پاک میکنیم تا کارکرد rewind متوقف شود. اگر گام آخر را انجام ندهیم ویدئو تا همیشه به سمت عقب میرود.
اگر فاصله زمان جاری با ابتدای ویدئو کمتر از 3 ثانیه نباشد، با اجرای کد media.currentTime -= 3 سه ثانیه از زمان جاری کم میکنیم. با این کار در عمل ویدئو را هر 200 میلیثانیه یک بار 3 ثانیه به عقب میکشیم.
بهروزرسانی زمان سپری شده
آخرین بخش از کدنویسی پخشکننده ویدئو، پیادهسازی نمایش زمان سپریشده از ویدئو است. برای انجام این کار یک تابع اجرا میکنیم که نمایش زمان را هر بار که رویداد timeupdate روی عنصر <video> اجرا شود بهروزرسانی میکند. فراوانی صدور این رویداد به مرورگر، توان CPU و موارد دیگری بستگی دارد.
کد ()addEventListener زیر را در ادامه کدهای دیگر اضافه کنید:
اکنون تابع ()setTime را تعریف میکنیم. کد زیر را به انتهای فایل اضافه کنید:
این تابع نسبتاً بلندی است. بنابراین آن را گام به گام بررسی میکنیم.
قبل از هر چیز تعداد دقیقهها و ثانیهها را در مقدار HTMLMediaElement.currentTime محاسبه میکنیم. سپس دو متغیر دیگر به نامهای minuteValue و secondValue مقداردهی میکنیم. دو گزاره if تعیین میکنند که آیا دقیقهها و ثانیهها کمتر از 10 هستند یا نه. اگر چنین باشد، یک صفر ابتدایی به مقادیر اضافه میکنیم تا شبیه به نحوه نمایش زمان در ساعتهای دیجیتال باشد.
مقدار زمان واقعی که نمایش مییابد به صورت minuteValue به علاوه یک علامت دونقطه (:) و سپس مقدار secondValue است. مقدار Node.textContent تایمر روی مقدار زمان تعیین میشود و از این رو در UI نمایش مییابد. برای محاسبه طولی که برای <div> درونی تعیین میکنیم ابتدا باید عرض <div> بیرونی را حساب کنیم و سپس آن را در HTMLMediaElement.currentTime ضرب و بر HTMLMediaElement.duration کلی رسانه تقسیم کنیم.
عرض <div> درونی را برابر با طول نوار تعیینشده به علاوه عبارت px تعیین میکنیم تا برابر با تعداد پیکسل موردنظر تنظیم شود.
اصلاح پخش و مکث
یک مشکل مانده که باید اصلاح کنیم. اگر دکمههای play/pause یا stop زمانی که کارکرد rewind یا fast forward فعال هستند، فشرده شوند، کار نخواهند کرد. برای اصلاح این مشکل باید کارکرد دکمه rwd/fwd را لغو کنیم و ویدئو را طبق انتظار play/stop کنیم. به این منظور قبل از هر چیز خطوط زیر را درون تابع ()stopMedia هر کجا که دوست دارید اضافه کنید:
اکنون همان خطوط را این بار در ابتدای تابع ()playPauseMedia درست قبل از گزاره if اضافه کنید.
در این زمان باید خطوط معادل را از تابعهای ()windBackward و ()windForward حذف کنید، چون این کارکرد در تابع ()stopMedia پیادهسازی شده است.
نکته: کارایی این کد را با ایجاد یک تابع جداگانه که این سه خط را اجرا میکند و سپس فراخوانی آن در موارد نیاز میتوانید از این هم بیشتر بالا ببرید. در این حالت دیگر نیازی به تکرار این خطوط چندگانه در کد وجود ندارد. این کار را به عنوان یک تمرین بر عهده شما قرار میدهیم.
سخن پایانی
به نظر میرسد مواردی که در این راهنما آموختیم تا به اینجا کافی بوده است. API-ی HTMLMediaElement کارکرد ارزشمندی برای ایجاد پخشکنندههای ویدئو و صوت در اختیار ما قرار میدهد و تنها نوک قله یخ است. در ادامه برخی روشها برای بهبود مثال موجود ارائه میکنیم.
زمان نمایش یافته کنونی در صورتی که ویدئو یک ساعت یا بیشتر طول داشته باشد، از کار میافتد، چون آن را طوری طراحی کردهایم که صرفاً دقیقه و ثانیه را نمایش دهد. تلاش کنید تا مثال را به نحوی تغییر دهید که ساعت را نیز نمایش دهد.
از آنجا که عنصر <audio> همان کارکرد HTMLMediaElement را دارد میتوانید به سادگی پخشکنندهای برای عنصر <audio> نیز بسازید.
روشی پیدا کنید که عنصر <div> درونی تایمر را به یک نوار seek و اسکرول بار واقعی تبدیل کنید یعنی زمانی که هر جای این نوار کلیک میکنید به آن بخش از ویدئو منتقل شود. برای شروع کار میتوانید مقادیر X و Y عنصر را با استفاده از متد ()getBoundingClientRect در اضلاع چپ/راست و بالا/پایین پیدا کنید و مختصات محل کلیک ماوس را نیز میتونید در شیء رویداد کلیک که روی شیء Document فراخوانی میشود به صورت زیر بیابید:
به این ترتیب به پایان این مقاله میرسیم. برای مطالعه بخش بعدی این سری مقالات روی لینک زیر کلیک کنید:
اگر این مطلب برای شما مفید بوده است، آموزش های زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- گنجینه آموزشهای طراحی وب
- مجموعه آموزشهای برنامهنویسی
- چگونه برنامه نویس وب شویم؟ – بخش اول: فرانتاند (FrontEnd)
- مقدمهای بر API-های وب — راهنمای جامع
==