เว็บต้องการเสียง (มีรสนิยม) มากกว่านี้!
ไลบรารีนี้ใช้งานได้กับ React DOM เท่านั้น แต่ @remigallego ได้สร้างทางเลือกอื่นสำหรับ React Native! ลองดูเสียงแบบโต้ตอบ-พื้นเมือง-ใช้-เสียง
โครงการนี้เป็นแบบ "กึ่งบำรุงรักษา" ?
ฉันไม่มีแบนด์วิดท์ในขณะนี้เพื่อตรวจสอบปัญหา Edge Case หรือช่วยแก้ไขปัญหา แต่ฉันวางแผนที่จะอัปเดต React ที่สำคัญให้ทันสมัยอยู่เสมอ และแก้ไขปัญหาที่ทั้งร้ายแรงและพบบ่อย
หากคุณมีไอเดียเกี่ยวกับฟีเจอร์ต่างๆ หรือพบเจอกับนิสัยแปลกๆ ฉันขอแนะนำให้คุณแยกโปรเจ็กต์นี้และทำให้เป็นของคุณเอง! อาจดูน่ากลัว แต่แหล่งที่มาไม่ซับซ้อนเท่ากับแพ็คเกจ NPM อื่นๆ ฉันเลื่อนการทำงานด้านเสียงอย่างหนักทั้งหมดไปที่ Howler) หากคุณใช้ React มาระยะหนึ่งแล้วและรู้สึกสบายใจกับ hooks คุณควรรู้สึกเหมือนอยู่บ้านด้วยโค้ดของแพ็คเกจนี้
สามารถเพิ่มแพ็คเกจได้โดยใช้ เส้นด้าย :
yarn add use-sound
หรือใช้ NPM:
npm install use-sound
UMD build พร้อมใช้งานบน 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 build อื่น เช่น Next.js คุณอาจได้รับข้อผิดพลาดดังนี้:
คุณอาจต้องใช้ตัวโหลดที่เหมาะสมเพื่อจัดการกับไฟล์ประเภทนี้ ขณะนี้ยังไม่มีการกำหนดค่าตัวโหลดให้ประมวลผลไฟล์นี้
ปัญหาคือ Webpack (ตัวบันเดิลที่ใช้ในการสร้างบันเดิล JS) ไม่ทราบวิธีการประมวลผลไฟล์ MP3
หากคุณมีสิทธิ์เข้าถึงการกำหนดค่า Webpack คุณสามารถอัปเดตให้ใช้ตัวโหลดไฟล์ ซึ่งจะสร้างเส้นทางแบบไดนามิกและเข้าถึงได้แบบสาธารณะไปยังไฟล์
เครื่องมือส่วนใหญ่จะให้โฟลเดอร์ "สาธารณะ" (create-react-app, Next.js) หรือโฟลเดอร์ "static" (Gatsby) คุณสามารถวางไฟล์เสียงของคุณลงในนั้น จากนั้นใช้เส้นทางสตริง
ไฟล์เสียงที่คุณจะใช้กับ use-sound
เป็นไปตามกฎเดียวกันกับเนื้อหาคงที่อื่นๆ เช่น รูปภาพหรือแบบอักษร ทำตามคำแนะนำสำหรับเมตาเฟรมเวิร์กที่คุณเลือก:
เส้นทางเสียง Async? หาก URL ไปยังไฟล์เสียงของคุณโหลดแบบอะซิงโครนัส คุณอาจประสบปัญหาบางประการ นี่อาจไม่ใช่แพ็คเกจที่เหมาะสมสำหรับการใช้งานนั้น
เพื่อประโยชน์ของผู้ใช้ เบราว์เซอร์ไม่อนุญาตให้เว็บไซต์สร้างเสียงจนกว่าผู้ใช้จะโต้ตอบกับเว็บไซต์เหล่านั้น (เช่น โดยการคลิกที่บางสิ่ง) จะไม่มีเสียงเกิดขึ้นจนกว่าผู้ใช้จะคลิก แตะ หรือกระตุ้นบางสิ่ง
useSound
ใช้ประโยชน์จากสิ่งนี้: เนื่องจากเรารู้ว่าไม่จำเป็นต้องใช้เสียงทันทีขณะโหลด เราจึงสามารถโหลดการขึ้นต่อกันของบุคคลที่สามแบบ Lazy Load ได้
useSound
จะเพิ่ม gzip ประมาณ 1kb ไปยังบันเดิลของคุณ และจะดึงแพ็คเกจเพิ่มเติมแบบอะซิงโครนัสหลังจากโหลด ซึ่งจะมีความเร็วประมาณ 9kb gzip
หากผู้ใช้คลิกบางสิ่งที่ทำให้เกิดเสียงรบกวนก่อนที่จะโหลดและดึงข้อมูลการขึ้นต่อกันนี้ จะถือว่าไม่ต้องดำเนินการ (ทุกอย่างจะยังคงทำงาน แต่ไม่มีเอฟเฟกต์เสียงใดที่จะเล่น) จากประสบการณ์ของฉันสิ่งนี้หายากมาก
พิจารณาข้อมูลโค้ดต่อไปนี้:
const [ playbackRate , setPlaybackRate ] = React . useState ( 0.75 ) ;
const [ play ] = useSound ( '/path/to/sound' , { playbackRate } ) ;
playbackRate
ไม่เพียงทำหน้าที่เป็นค่า เริ่มต้น สำหรับเอฟเฟกต์เสียงเท่านั้น หาก playbackRate
เปลี่ยนแปลง เสียงจะเริ่มเล่นในอัตราใหม่ทันที นี่เป็นจริงสำหรับตัวเลือกทั้งหมดที่ส่งผ่านไปยัง useSound
hook
ตะขอ useSound
รับสองอาร์กิวเมนต์:
HookOptions
)มันสร้างอาร์เรย์ที่มีค่าสองค่า:
ExposedData
) เมื่อเรียกใช้ฟังก์ชันเพื่อเล่นเสียง คุณสามารถส่งผ่านชุดตัวเลือกได้ ( PlayOptions
)
มาดูแต่ละสิ่งเหล่านี้กัน
เมื่อเรียก useSound
คุณสามารถส่งผ่านตัวเลือกต่างๆ ได้:
ชื่อ | ค่า |
---|---|
ปริมาณ | ตัวเลข |
อัตราการเล่น | ตัวเลข |
ขัดจังหวะ | บูลีน |
เปิดใช้งานเสียงแล้ว | บูลีน |
เทพดา | สไปรท์แมป |
[ได้รับมอบหมาย] | - |
volume
คือตัวเลขตั้งแต่ 0
ถึง 1
โดยที่ 1
คือระดับเสียงเต็ม และ 0
จะถูกปิดเสียงโดยสิ้นเชิงplaybackRate
เป็นตัวเลขตั้งแต่ 0.5
ถึง 4
สามารถใช้เพื่อทำให้ตัวอย่างช้าลงหรือเร็วขึ้นได้ เช่นเดียวกับเครื่องเล่นแผ่นเสียง การเปลี่ยนแปลงความเร็วก็ส่งผลต่อระดับเสียงเช่นกันinterrupt
ระบุว่าเสียงควรจะ "ทับซ้อนกัน" หรือไม่หากฟังก์ชัน play
ถูกเรียกอีกครั้งก่อนที่เสียงจะสิ้นสุดsoundEnabled
ช่วยให้คุณสามารถส่งผ่านค่า (โดยทั่วไปจากบริบทหรือ redux หรือบางอย่าง) เพื่อปิดเสียงทั้งหมด โปรดทราบว่าสิ่งนี้สามารถแทนที่ได้ใน PlayOptions
ดูด้านล่างsprite
ช่วยให้คุณใช้ตะขอ useSound
ครั้งเดียวสำหรับเอฟเฟกต์เสียงหลายแบบ ดู "สไปรท์" ด้านล่าง [delegated]
หมายถึงข้อเท็จจริงที่ว่าอาร์กิวเมนต์เพิ่มเติมใด ๆ ที่คุณส่งใน HookOptions
จะถูกส่งต่อไปยังตัวสร้าง Howl
ดู "ช่องหลบหนี" ด้านล่างสำหรับข้อมูลเพิ่มเติม
play
เมื่อเรียก hook คุณจะได้รับฟังก์ชัน play กลับมาเป็นรายการแรกใน tuple:
const [ play ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
คุณสามารถเรียกใช้ฟังก์ชันนี้โดยไม่มีข้อโต้แย้งเมื่อคุณต้องการกระตุ้นเสียง คุณยังสามารถเรียกมันด้วยวัตถุ PlayOptions
:
ชื่อ | ค่า |
---|---|
รหัส | เชือก |
เปิดใช้งาน ForceSound แล้ว | บูลีน |
อัตราการเล่น | ตัวเลข |
id
ใช้สำหรับระบุตัวตนสไปรท์ ดู "สไปรท์" ด้านล่างforceSoundEnabled
ช่วยให้คุณสามารถแทนที่ soundEnabled
boolean ที่ส่งผ่านไปยัง HookOptions
โดยทั่วไปคุณไม่เคยต้องการทำเช่นนี้ ข้อยกเว้นเดียวที่ฉันพบ: การเรียกเสียงบนปุ่ม "ปิดเสียง"playbackRate
เป็นอีกวิธีหนึ่งในการตั้งค่าอัตราการเล่นใหม่ เช่นเดียวกับใน HookOptions
โดยทั่วไปคุณควรทำผ่าน HookOptions
มากกว่า นี่คือช่องทางหลบหนี hook สร้างทูเพิลที่มี 2 ตัวเลือก ได้แก่ ฟังก์ชัน play และออบเจ็กต์ ExposedData
:
const [ play , exposedData ] = useSound ( '/meow.mp3' ) ;
// ^ What we're talking about
ชื่อ | ค่า |
---|---|
หยุด | ฟังก์ชั่น ((id?: string) => เป็นโมฆะ) |
หยุดชั่วคราว | ฟังก์ชั่น ((id?: string) => เป็นโมฆะ) |
ระยะเวลา | หมายเลข (หรือ null) |
เสียง | เสียงหอน (หรือ null) |
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
สำหรับเสียงแต่ละเสียง ค่าคือสิ่งอันดับ (อาร์เรย์ที่มีความยาวคงที่) โดยมี 2 รายการ:
การแสดงภาพนี้อาจทำให้ชัดเจนยิ่งขึ้น:
เราสามารถส่ง 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 >
) ;
} ;