Skip to content

Commit

Permalink
feat: allow use shortcut keys without controller
Browse files Browse the repository at this point in the history
  • Loading branch information
ambar committed Jan 29, 2022
1 parent f692e04 commit 99ae812
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 189 deletions.
169 changes: 11 additions & 158 deletions packages/griffith/src/components/Controller.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import React, {useContext, useEffect, useRef, useState} from 'react'
import React, {useState} from 'react'
import {css} from 'aphrodite/no-important'
import clamp from 'lodash/clamp'
import * as displayIcons from './icons/display/index'
import * as controllerIcons from './icons/controller/index'
import {ProgressDot} from '../types'
import PlayButtonItem from './items/PlayButtonItem'
import TimelineItem from './items/TimelineItem'
Expand All @@ -16,8 +14,6 @@ import PlaybackRateMenuItem from './items/PlaybackRateMenuItem'
import PageFullScreenButtonItem from './items/PageFullScreenButtonItem'
import useHandler from '../hooks/useHandler'
import useBoolean from '../hooks/useBoolean'
import {useActionToastDispatch} from './ActionToast'
import VideoSourceContext from '../contexts/VideoSourceContext'

type ControllerProps = {
standalone?: boolean
Expand All @@ -31,11 +27,11 @@ type ControllerProps = {
isPip: boolean
onDragStart?: () => void
onDragEnd?: () => void
onPlay?: () => void
onPause?: () => void
onTogglePlay?: () => void
onSeek?: (currentTime: number) => void
onQualityChange?: (...args: any[]) => any
onVolumeChange?: (volume: number) => void
onToggleMuted?: () => void
onToggleFullScreen?: () => void
onTogglePageFullScreen?: () => void
onTogglePip?: (...args: any[]) => void
Expand Down Expand Up @@ -89,7 +85,6 @@ function Controller(props: ControllerProps) {
onTogglePageFullScreen,
onTogglePip,
showPip,
standalone,
progressDots,
hiddenPlayButton,
hiddenTimeline,
Expand All @@ -101,166 +96,24 @@ function Controller(props: ControllerProps) {
shouldShowPageFullScreenButton,
onProgressDotHover,
onProgressDotLeave,
onPause,
onPlay,
onTogglePlay,
onSeek,
onToggleMuted,
onVolumeChange,
} = props
const {playbackRates, currentPlaybackRate, setCurrentPlaybackRate} =
useContext(VideoSourceContext)
const actionToastDispatch = useActionToastDispatch()

const [isVolumeHovered, isVolumeHoveredSwitch] = useBoolean()
const [slideTime, setSlideTime] = useState<number>()
const prevVolumeRef = useRef(1)

const rotatePlaybackRate = (dir: 'next' | 'prev') => {
const index = playbackRates?.findIndex(
(x) => x.value === currentPlaybackRate.value
)
if (index >= 0) {
const next = playbackRates[index + (dir === 'next' ? 1 : -1)]
if (next) {
actionToastDispatch({icon: displayIcons.play, label: next.text})
setCurrentPlaybackRate(next)
}
}
}

const handleDragMove = useHandler((slideTime: number) => {
setSlideTime(clamp(slideTime, 0, duration))
})

const handleTogglePlay = () => {
if (isPlaying) {
onPause?.()
} else {
onPlay?.()
}
}

const handleSeek = useHandler((currentTime: number) => {
currentTime = clamp(currentTime, 0, duration)
if (onSeek) {
onSeek(currentTime)
setSlideTime(void 0)
}
onSeek?.(clamp(currentTime, 0, duration))
setSlideTime(void 0)
})

const handleVolumeChange = useHandler((value: number, showToast = false) => {
value = clamp(value, 0, 1)
if (showToast) {
actionToastDispatch({
icon: value ? controllerIcons.volume : controllerIcons.muted,
label: `${(value * 100).toFixed(0)}%`,
})
}
onVolumeChange?.(value)
})

const handleToggleMuted = useHandler((showToast = false) => {
if (volume) {
prevVolumeRef.current = volume
}
handleVolumeChange(volume ? 0 : prevVolumeRef.current, showToast)
})

const handleKeyDown = useHandler((event: KeyboardEvent) => {
// 防止冲突,有修饰键按下时不触发自定义热键
if (event.altKey || event.ctrlKey || event.metaKey) {
return
}
let handled = true

switch (event.key) {
case ' ':
case 'k':
case 'K':
actionToastDispatch({
icon: isPlaying ? displayIcons.pause : displayIcons.play,
})
handleTogglePlay()
break

case 'Enter':
case 'f':
case 'F':
onToggleFullScreen?.()
break
case 'Escape':
if (isPageFullScreen) {
onTogglePageFullScreen?.()
}
break
case 'ArrowLeft':
handleSeek(currentTime - 5)
break

case 'ArrowRight':
handleSeek(currentTime + 5)
break

case 'j':
case 'J':
handleSeek(currentTime - 10)
break

case 'l':
case 'L':
handleSeek(currentTime + 10)
break
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
handleSeek((duration / 10) * Number(event.key))
break

case 'm':
case 'M':
handleToggleMuted(true)
break

case 'ArrowUp':
// 静音状态下调整可能不切换为非静音更好(设置一成临时的,切换后再应用临时状态)
handleVolumeChange(volume + 0.05, true)
break

case 'ArrowDown':
handleVolumeChange(volume - 0.05, true)
break

case '<':
rotatePlaybackRate('prev')
break

case '>':
rotatePlaybackRate('next')
break

default:
handled = false
break
}
if (handled) {
event.preventDefault()
}
})

useEffect(() => {
if (standalone) {
document.addEventListener('keydown', handleKeyDown)
return () => {
document.removeEventListener('keydown', handleKeyDown)
}
}
}, [handleKeyDown, standalone])

const displayedCurrentTime = slideTime || currentTime

return (
Expand All @@ -284,7 +137,7 @@ function Controller(props: ControllerProps) {
{!hiddenPlayButton && (
<PlayButtonItem
isPlaying={isPlaying}
onClick={() => handleTogglePlay()}
onClick={() => onTogglePlay?.()}
/>
)}
{hiddenTimeline && <div className={css(styles.timelineHolder)} />}
Expand Down Expand Up @@ -318,8 +171,8 @@ function Controller(props: ControllerProps) {
menuShown={isVolumeHovered}
onMouseEnter={isVolumeHoveredSwitch.on}
onMouseLeave={isVolumeHoveredSwitch.off}
onToggleMuted={handleToggleMuted}
onChange={handleVolumeChange}
onToggleMuted={onToggleMuted}
onChange={onVolumeChange}
/>
)}
</div>
Expand Down
Loading

0 comments on commit 99ae812

Please sign in to comment.