Greasy Fork is available in English.
左撇子视频快捷键
当前为
// ==UserScript==
// @name WASD
// @namespace http://github.com/dnzng
// @version 0.0.1
// @description Left-Handed Video Shortcuts
// @description:zh-CN 左撇子视频快捷键
// @author Dylan Zhang
// @license MIT
// @include https://*.youtube.com/*
// @include https://*.bilibili.com/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// ==/UserScript==
(function() {
'use strict';
/* utilities */
const utils = {
ensureCondition(condition, maxAttempts = 600 /* 10s */, failureMessage) {
return new Promise((resolve, reject) => {
let attempts = 0
const detect = () => {
const result = condition()
if (result) {
resolve(result)
} else if (attempts < maxAttempts) {
attempts++
requestAnimationFrame(detect)
} else {
reject(new Error(failureMessage))
}
}
requestAnimationFrame(detect)
})
},
ensureElement(selector, maxAttempts = 600) {
return utils.ensureCondition(
() => document.querySelector(selector),
maxAttempts,
`Could not detect ${selector} after ${maxAttempts} attempts`
)
}
}
class Indicator {
constructor() {
this.el = null
this.timer = null
this.duration = 0.5
this.activeClass = 'wasd-indicator-active'
this.initialize()
}
initialize() {
this.injectStyle()
this.injectElement()
}
injectStyle() {
const style = document.createElement('style')
style.id = 'wasd'
style.textContent = `
#wasd-indicator {
display: flex;
justify-content: center;
align-items: center;
width: 50px;
height: 50px;
background: #000;
font-size: 18px;
font-weight: bold;
color: #fff;
border-radius: 10px;
opacity: 0;
transition: opacity ${this.duration}s ease;
position: fixed;
left: 10px;
bottom: 10px;
z-index: -1;
}
#wasd-indicator.${this.activeClass} \{
opacity: 1;
z-index: 99;
}
`
document.body.appendChild(style)
}
injectElement() {
const el = document.createElement('div')
el.id = 'wasd-indicator'
document.body.appendChild(this.el = el)
}
show(text) {
const { el, activeClass } = this
el.textContent = text
el.classList.add(activeClass)
if (this.timer) clearTimeout(this.timer)
this.timer = setTimeout(() => {
el.classList.remove(activeClass)
this.timer = null
}, 800)
}
}
class Shortcuts {
constructor(meida, indicator) {
this.media = meida
this.indicator = indicator
this.isVisible = false
this.seekStep = 5
this.volumeStep = 0.1
this.allowKeysList = {
w: 'W',
s: 'S',
a: 'A',
d: 'D',
x: ['关闭', '显示']
}
this.bindEvents()
}
bindEvents() {
window.addEventListener('keydown', this.handleKeydown.bind(this), { capture: true })
}
handleKeydown(event) {
const { key } = event
const { allowKeysList } = this
if (this.isTyping()) return
if (!allowKeysList[key]) return
if (this.isVisible) {
if (key === 'x') {
this.isVisible = false
this.indicator.show(allowKeysList[key][0])
} else {
this.indicator.show(allowKeysList[key])
}
} else {
if (key === 'x') {
this.isVisible = true
this.indicator.show(allowKeysList[key][1])
}
}
event.stopImmediatePropagation()
switch(key) {
case 'w': // increase volume
this.increaseVolume()
break
case 's': // decrease volume
this.decreaseVolume()
break
case 'a': // rewind
this.seek(this.getCurrentTime() - this.seekStep)
break
case 'd': // fast forward
this.seek(this.getCurrentTime() + this.seekStep)
break
}
}
seek(time) {
this.media.currentTime = time
}
getCurrentTime() {
return this.media.currentTime
}
increaseVolume() {
this.media.volume = Math.min(this.media.volume + this.volumeStep, 1)
}
decreaseVolume() {
this.media.volume = Math.max(this.media.volume - this.volumeStep, 0)
}
togglePlay() {
const { media } = this
media.paused
? media.play()
: media.pause()
}
isTyping() {
const activeElement = document.activeElement
return activeElement instanceof HTMLInputElement ||
activeElement instanceof HTMLTextAreaElement ||
activeElement.isContentEditable === true
}
}
utils.ensureElement('video').then(video => {
new Shortcuts(video, new Indicator())
})
})();