يحتاج الويب إلى المزيد من الأصوات (اللذيذة)!
تعمل هذه المكتبة فقط مع React DOM، لكن @remigallego قام بإنشاء بديل لـ React Native! تحقق من صوت الاستخدام الأصلي.
هذا المشروع "شبه صيانة"؟
ليس لدي النطاق الترددي الآن للنظر في مشكلات حالة الحافة أو المساعدة في استكشاف الأخطاء وإصلاحها، لكنني أخطط لإبقائها محدثة مع إصدارات React الرئيسية، وإصلاح المشكلات الخطيرة والشائعة.
إذا كانت لديك أفكار للميزات، أو واجهت مراوغات غريبة، فإنني أوصي بشدة بتقسيم المشروع وجعله خاصًا بك! قد يبدو الأمر مخيفًا، لكن المصدر ليس معقدًا مثل العديد من حزم NPM الأخرى؛ أؤجل كل العمل الصوتي الصعب إلى Howler). إذا كنت تستخدم React منذ فترة وتشعر بالراحة مع الخطافات، فيجب أن تشعر وكأنك في بيتك مع رمز هذه الحزمة.
يمكن إضافة الحزمة باستخدام الغزل :
yarn add use-sound
أو استخدم NPM:
npm install use-sound
بناء UMD متاح على unpkg.
إذا كان مشروعك يستخدم TypeScript، فيجب عليك أيضًا تثبيت حزمة @types/howler
باعتبارها تبعية للتطوير.
يتضمن البرنامج التعليمي العديد من العروض التوضيحية، بالإضافة إلى تعليمات للعثور على المؤثرات الصوتية وإعدادها. إنه مكان رائع للبدء.
يمكنك أيضًا الاطلاع على كتاب القصص الذي يتضمن الكثير من الأمثلة السريعة.
import useSound from 'use-sound' ;
import boopSfx from '../../sounds/boop.mp3' ;
const BoopButton = ( ) => {
const [ play ] = useSound ( boopSfx ) ;
return < button onClick = { play } > Boop! < / button > ;
} ;
يقوم هذا العرض التوضيحي بتشغيل الصوت فقط أثناء التمرير فوق أحد العناصر. يتوقف الصوت مؤقتًا عندما يترك الماوس العنصر:
ملاحظة: تقوم العديد من المتصفحات بتعطيل الأصوات حتى يقوم المستخدم بالنقر فوق مكان ما في الصفحة. إذا لم تسمع أي شيء في هذا المثال، فحاول النقر في أي مكان ثم حاول مرة أخرى.
import useSound from 'use-sound' ;
import fanfareSfx from '../../sounds/fanfare.mp3' ;
const FanfareButton = ( ) => {
const [ play , { stop } ] = useSound ( fanfareSfx ) ;
return (
< button onMouseEnter = { ( ) => play ( ) } onMouseLeave = { ( ) => stop ( ) } >
< span role = "img" aria-label = "trumpet" >
?
< / span >
< / button >
) ;
} ;
باستخدام خيار playbackRate
، يمكنك تغيير سرعة/درجة صوت العينة. يقوم هذا المثال بتشغيل صوت ويجعله أسرع بنسبة 10% في كل مرة:
import useSound from 'use-sound' ;
import glugSfx from '../../sounds/glug.mp3' ;
export const RisingPitch = ( ) => {
const [ playbackRate , setPlaybackRate ] = React . useState ( 0.75 ) ;
const [ play ] = useSound ( glugSfx , {
playbackRate ,
// `interrupt` ensures that if the sound starts again before it's
// ended, it will truncate it. Otherwise, the sound can overlap.
interrupt : true ,
} ) ;
const handleClick = ( ) => {
setPlaybackRate ( playbackRate + 0.1 ) ;
play ( ) ;
} ;
return (
< Button onClick = { handleClick } >
< span role = "img" aria-label = "Person with lines near mouth" >
?
< / span >
< / Button >
) ;
} ;
يتطلب useSound
مسارًا إلى ملف صوتي، وليس من الواضح كيفية توفيره في تطبيق React.
باستخدام create-react-app
، يمكنك "استيراد" ملف MP3. سيتم حله إلى مسار تم إنشاؤه ديناميكيًا:
import someAudioFile from '../sounds/sound.mp3' ;
console . log ( someAudioFile ) ; // “/build/sounds/sound-abc123.mp3”
إذا حاولت استخدام هذه الخدعة في نظام إنشاء React آخر مثل Next.js، فقد تحصل على خطأ مثل هذا:
قد تحتاج إلى مُحمل مناسب للتعامل مع هذا النوع من الملفات، حاليًا لم يتم تكوين أي أدوات تحميل لمعالجة هذا الملف.
المشكلة هي أن Webpack (المجمع المستخدم أسفل الغطاء لإنشاء حزم JS) لا يعرف كيفية معالجة ملف MP3.
إذا كان لديك حق الوصول إلى تكوين Webpack، فيمكنك تحديثه لاستخدام أداة تحميل الملفات، مما سيؤدي إلى إنشاء مسار ديناميكي يمكن الوصول إليه بشكل عام إلى الملف.
وبدلاً من ذلك، ستمنحك معظم الأدوات مجلدًا "عامًا" (create-react-app، Next.js) أو مجلدًا "ثابتًا" (Gatsby). يمكنك إسقاط ملفاتك الصوتية هناك، ثم استخدام مسار السلسلة.
تتبع ملفات الصوت التي ستستخدمها مع use-sound
نفس القواعد التي تتبعها الأصول الثابتة الأخرى مثل الصور أو الخطوط. اتبع الإرشادات الخاصة بالإطار التعريفي الذي تختاره:
️ مسارات الصوت غير المتزامنة؟️ إذا تم تحميل عنوان URL لملفك الصوتي بشكل غير متزامن، فقد تواجه بعض المشكلات. ربما لا تكون هذه الحزمة المناسبة لحالة الاستخدام هذه.
من أجل مصلحة المستخدم، لا تسمح المتصفحات لمواقع الويب بإصدار الصوت حتى يتفاعل المستخدم معها (على سبيل المثال، بالنقر فوق شيء ما). لن يتم إنتاج أي صوت حتى ينقر المستخدم على شيء ما، أو ينقر عليه، أو يقوم بتشغيله.
يستفيد useSound
من هذا: نظرًا لأننا نعلم أنه لن تكون هناك حاجة إلى الأصوات فورًا عند التحميل، فيمكننا التحميل البطيء لتبعية جهة خارجية.
سيضيف useSound
حوالي 1 كيلو بايت gzip إلى الحزمة الخاصة بك، وسيجلب بشكل غير متزامن حزمة إضافية بعد التحميل، والتي تصل إلى حوالي 9 كيلو بايت gzip.
إذا نقر المستخدم على شيء يُصدر ضوضاء قبل تحميل هذه التبعية وجلبها، فسيكون ذلك محظورًا (سيظل كل شيء يعمل، ولكن لن يتم تشغيل أي تأثير صوتي). في تجربتي هذا أمر نادر للغاية.
خذ بعين الاعتبار مقتطف التعليمات البرمجية التالي:
const [ playbackRate , setPlaybackRate ] = React . useState ( 0.75 ) ;
const [ play ] = useSound ( '/path/to/sound' , { playbackRate } ) ;
لا يعمل playbackRate
كقيمة أولية للتأثير الصوتي فقط. إذا تغير playbackRate
، فسيبدأ تشغيل الصوت على الفور بمعدل جديد. وينطبق هذا على جميع الخيارات التي تم تمريرها إلى الخطاف useSound
.
يأخذ الخطاف useSound
وسيطتين:
HookOptions
)ينتج مصفوفة بقيمتين:
ExposedData
) عند استدعاء الوظيفة لتشغيل الصوت، يمكنك تمرير مجموعة من الخيارات ( PlayOptions
) إليها.
دعونا نذهب من خلال كل من هذه بدورها.
عند الاتصال بـ useSound
، يمكنك تمرير مجموعة متنوعة من الخيارات إليه:
اسم | قيمة |
---|---|
مقدار | رقم |
معدل التشغيل | رقم |
يقاطع | منطقية |
soundEnabled | منطقية |
شبح | خريطة العفريت |
[مفوض] | — |
volume
هو رقم من 0
إلى 1
، حيث 1
هو الحجم الكامل و 0
مكتوم تمامًا.playbackRate
هو رقم من 0.5
إلى 4
. ويمكن استخدامه لإبطاء أو تسريع العينة. مثل القرص الدوار، تؤثر التغييرات في السرعة أيضًا على درجة الصوت.interrupt
ما إذا كان الصوت يجب أن يكون قادرًا على "التداخل" أم لا إذا تم استدعاء وظيفة play
مرة أخرى قبل انتهاء الصوت.soundEnabled
بتمرير قيمة (عادةً من السياق أو الإعادة أو شيء من هذا القبيل) لكتم جميع الأصوات. لاحظ أنه يمكن تجاوز هذا في PlayOptions
، انظر أدناهsprite
استخدام خطاف useSound
واحد لتأثيرات صوتية متعددة. انظر "العفاريت" أدناه. يشير [delegated]
إلى حقيقة أن أي وسيطة إضافية تمررها في HookOptions
ستتم إعادة توجيهها إلى مُنشئ Howl
. راجع "فتحات الهروب" أدناه لمزيد من المعلومات.
play
عند استدعاء الخطاف، يمكنك استعادة وظيفة التشغيل كأول عنصر في المجموعة:
const [ play ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
يمكنك استدعاء هذه الوظيفة بدون أية وسائط عندما تريد تشغيل الصوت. يمكنك أيضًا الاتصال به باستخدام كائن PlayOptions
:
اسم | قيمة |
---|---|
بطاقة تعريف | خيط |
forceSoundEnabled | منطقية |
معدل التشغيل | رقم |
id
لتحديد هوية الكائنات. انظر "العفاريت" أدناه.forceSoundEnabled
تجاوز القيمة المنطقية soundEnabled
التي تم تمريرها إلى HookOptions
. أنت عموما لا تريد أن تفعل هذا أبدا. الاستثناء الوحيد الذي وجدته: تشغيل الصوت على زر "كتم الصوت".playbackRate
طريقة أخرى يمكنك من خلالها تعيين معدل تشغيل جديد، كما هو الحال في HookOptions
. بشكل عام، يجب أن تفضل القيام بذلك من خلال HookOptions
، وهذا هو منفذ الهروب. يُنتج الخطاف صفًا يحتوي على خيارين، وظيفة التشغيل وكائن ExposedData
:
const [ play , exposedData ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
اسم | قيمة |
---|---|
قف | الوظيفة ((المعرف؟: سلسلة) => باطلة) |
يوقف | الوظيفة ((المعرف؟: سلسلة) => باطلة) |
مدة | رقم (أو فارغ) |
صوت | عواء (أو فارغة) |
stop
هي وظيفة يمكنك استخدامها لإيقاف الصوت بشكل استباقي.pause
يشبه stop
، إلا أنه يمكن استئنافه من نفس النقطة. ما لم تكن تعلم أنك تريد الاستئناف، فيجب عليك استخدام stop
؛ pause
موارد الخنازير مؤقتًا، حيث من المتوقع أن يتم استئنافها في مرحلة ما.duration
هي طول العينة بالمللي ثانية. سيكون null
حتى يتم تحميل العينة. لاحظ أنه بالنسبة للعفاريت، فهو طول الملف بأكمله.sound
هو فتحة الهروب. يمنحك الوصول إلى مثيل Howl
الأساسي. راجع وثائق Howler لمعرفة المزيد حول كيفية استخدامه. لاحظ أن هذا سيكون null
في اللحظات القليلة الأولى بعد تحميل المكون. الكائن الصوتي هو ملف صوتي واحد يحتوي على عينات متعددة. بدلاً من تحميل العديد من الأصوات الفردية، يمكنك تحميل ملف واحد وتقسيمه إلى أقسام متعددة يمكن تشغيلها بشكل مستقل.
يمكن أن تكون هناك فائدة في الأداء لهذا، نظرًا لأنه يتطلب عددًا أقل من طلبات الشبكة المتوازية، ولكن قد يكون من المفيد أيضًا القيام بذلك إذا كان مكون واحد يحتاج إلى عينات متعددة. انظر قصة Drum Machine للحصول على مثال.
بالنسبة للعفاريت، سنحتاج إلى تعريف SpriteMap
. يبدو مثل هذا:
const spriteMap = {
laser : [ 0 , 300 ] ,
explosion : [ 1000 , 300 ] ,
meow : [ 2000 , 75 ] ,
} ;
SpriteMap
هو كائن. المفاتيح هي id
الأصوات الفردية. القيمة عبارة عن صف (مصفوفة ذات طول ثابت) تحتوي على عنصرين:
قد يجعل هذا التصور الأمر أكثر وضوحًا:
يمكننا تمرير SpriteMap الخاص بنا كأحد خيارات HookOptions الخاصة بنا:
const [ play ] = useSound ( '/path/to/sprite.mp3' , {
sprite : {
laser : [ 0 , 300 ] ,
explosion : [ 1000 , 300 ] ,
meow : [ 2000 , 75 ] ,
} ,
} ) ;
لتشغيل كائن معين، سنقوم بتمرير id
عند استدعاء وظيفة play
:
< button
onClick = { ( ) => play ( { id : 'laser' } ) }
>
Howler هي مكتبة قوية جدًا، وقد كشفنا فقط عن شريحة صغيرة مما يمكنها فعله في useSound
. نكشف عن فتحتين للهروب لتمنحك المزيد من التحكم.
أولاً، أي خيار غير معروف تمرره إلى HookOptions
سيتم تفويضه إلى Howl
. يمكنك الاطلاع على القائمة الكاملة للخيارات في مستندات Howler. فيما يلي مثال لكيفية استخدام onend
لتشغيل وظيفة عندما يتوقف الصوت عن التشغيل:
const [ play ] = useSound ( '/thing.mp3' , {
onend : ( ) => {
console . info ( 'Sound ended!' ) ;
} ,
} ) ;
إذا كنت بحاجة إلى مزيد من التحكم، فيجب أن تكون قادرًا على استخدام كائن sound
مباشرة، وهو مثال على Howler.
على سبيل المثال: يعرض Howler طريقة fade
، والتي تتيح لك تلاشي الصوت للداخل أو للخارج. يمكنك استدعاء هذه الطريقة مباشرة على كائن sound
:
const Arcade = ( ) => {
const [ play , { sound } ] = useSound ( '/win-theme.mp3' ) ;
return (
< button
onClick = { ( ) => {
// You win! Fade in the victory theme
sound . fade ( 0 , 1 , 1000 ) ;
} }
>
Click to win
< / button >
) ;
} ;