const getPosition = element => { let x = 0 let y = 0 while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { x += element.offsetLeft - element.scrollLeft y += element.offsetTop - element.scrollTop element = element.offsetParent } return { x: x, y: y } } const secondsToTime = sec => { sec = Math.abs(sec) const hours = Math.floor(sec / 3600) const minutes = Math.floor((sec - hours * 3600) / 60) const seconds = sec - hours * 3600 - minutes * 60 let result = fixed(seconds) + '秒' if (minutes > 0) { result = minutes + '分' + result } if (hours > 0) { result = hours + '时' + result } return result } const secondsToHms = sec => { sec = Math.abs(sec) const hours = Math.floor(sec / 3600) const minutes = Math.floor((sec - hours * 3600) / 60) const seconds = sec - hours * 3600 - minutes * 60 let result = (seconds < 10 ? '0' : '') + fixed(seconds) result = (minutes < 10 ? '0' : '') + minutes + ':' + result result = (hours < 10 ? '0' : '') + hours + ':' + result return result } class Swiper { constructor (element) { this.action = new SwipeAction(element) this.onTouchStart = null this.onTouchEnd = null this.direction = null element.addEventListener('touchstart', e => { this.xDown = e.touches[0].clientX this.yDown = e.touches[0].clientY if (this.onTouchStart) { this.onTouchStart(e) } }) element.addEventListener('touchmove', e => { if (!this.xDown || !this.yDown) { return } const xUp = e.touches[0].clientX const yUp = e.touches[0].clientY const elementPosition = getPosition(element) const position = { x: (e.touches[0].pageX - elementPosition.x) / element.clientWidth, y: (e.touches[0].pageY - elementPosition.y) / element.clientHeight, width: element.clientWidth, height: element.clientHeight } const xDiff = this.xDown - xUp const yDiff = this.yDown - yUp if (!this.direction) { let direction = '' if (Math.abs(xDiff) > Math.abs(yDiff)) { direction = 'horizontal' } else { direction = 'vertical' } this.direction = direction e.preventDefault() } else { if (this.direction === 'vertical') { this.action.startAction(this.direction, yDiff, position) } else if (this.direction === 'horizontal') { this.action.startAction(this.direction, -xDiff, position) } e.preventDefault() } }) element.addEventListener('touchend', e => { this.xDown = null this.yDown = null this.direction = null if (this.onTouchEnd) { this.onTouchEnd(e) } }) } } class SwipeAction { constructor (element) { this.lowSpeedForward = null this.lowSpeedBackward = null this.mediumSpeedForward = null this.mediumSpeedBackward = null this.highSpeedForward = null this.highSpeedBackward = null this.volumeUp = null this.volumeDown = null this.brightnessUp = null this.brightnessDown = null this.speedCancel = null this.minSwipeDistance = 20 this.onActionStart = null this.onActionEnd = null this.element = element this.touchStart = false this.startPosition = null this.lastAction = null element.addEventListener('touchstart', () => { this.touchStart = true }) element.addEventListener('touchend', () => { this.startPosition = null this.onActionEnd && this.onActionEnd(this.lastAction) this.lastAction = null }) } startAction (direction, distance, position) { if (this.touchStart) { this.onActionStart && this.onActionStart(direction) this.startPosition = position this.touchStart = false } if (direction === 'vertical') { // if (Math.abs(distance) < this.minSwipeDistance) { // this.volumeCancel && this.volumeCancel() // this.lastAction = null // } else { let upHandler let downHandler if (this.startPosition.x < 1 / 2) { upHandler = this.brightnessUp downHandler = this.brightnessDown const brightnessChange = Math.round( 200 * (distance - this.minSwipeDistance) / (1.5 * position.height) ) if (distance > 0) { upHandler && upHandler(brightnessChange) } else { downHandler && downHandler(brightnessChange) } this.lastAction = { type: 'brightness', brightness: brightnessChange } } else { upHandler = this.volumeUp downHandler = this.volumeDown const volumeChange = Math.round( 200 * (distance - this.minSwipeDistance) / (1.5 * position.height) ) if (distance > 0) { upHandler && upHandler(volumeChange) } else { downHandler && downHandler(volumeChange) } this.lastAction = { type: 'volume', volume: volumeChange } } // } } else if (direction === 'horizontal') { if (position.y < 1 / 3 && (position.x < 0.1 || position.x > 0.9) // || Math.abs(distance) < this.minSwipeDistance ) { this.speedCancel && this.speedCancel() this.lastAction = null } else { let speedFactor = 0 let forwardHandler let backwardHandler if (this.startPosition.y < 1 / 3) { speedFactor = 0.05 forwardHandler = this.lowSpeedForward backwardHandler = this.lowSpeedBackward } else if (this.startPosition.y >= 1 / 3 && this.startPosition.y <= 2 / 3) { speedFactor = 0.2 forwardHandler = this.mediumSpeedForward backwardHandler = this.mediumSpeedBackward } else { speedFactor = 1 forwardHandler = this.highSpeedForward backwardHandler = this.highSpeedBackward } if (distance > 0) { const seconds = (distance - this.minSwipeDistance) * speedFactor forwardHandler && forwardHandler(seconds) this.lastAction = { type: 'playback', seconds: seconds } } else { const seconds = (distance + this.minSwipeDistance) * speedFactor backwardHandler && backwardHandler(seconds) this.lastAction = { type: 'playback', seconds: seconds } } } } } } class VideoShot { constructor () { this.aid = unsafeWindow.aid this.cid = unsafeWindow.cid this.cidData = null this.supportWebp = VideoShot.supportWebp } getVideoshot (currentTime, done) { if (!(this.aid && this.cid)) { return } if (!this.cidData) { Ajax.getText(`https://api.bilibili.com/x/player/videoshot?aid=${this.aid}&cid=${this.cid}&index=1`).then(response => { this.cidData = JSON.parse(response).data this.getVideoshot(currentTime, done) }) } else { const data = this.cidData const indexData = data.index let shotIndex = 0 for (let index = 0; index < indexData.length - 2; index++) { if (currentTime >= indexData[index] && currentTime < indexData[index + 1]) { shotIndex = index break } } let imageData = data.image if (this.supportWebp) { imageData = imageData.map(url => url.replace('.jpg', '.jpg@.webp')) } const xLength = parseInt(data.pv_x_len) || 10 const yLength = parseInt(data.pv_y_len) || 10 const xSize = parseInt(data.pv_x_size) || 160 const ySize = parseInt(data.pv_y_size) || 90 const x = -(shotIndex % 100 % xLength) * xSize const y = -Math.floor(shotIndex % 100 / yLength) * ySize done({ width: xSize, height: ySize, backgroundImage: `url(${imageData[Math.floor(shotIndex / 100)]})`, backgroundPosition: `${x}px ${y}px` }) } } static get supportWebp () { try { const canvas = document.createElement('canvas') if (canvas.getContext && canvas.getContext('2d')) { try { return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0 } catch (ex) { return false } } else { return false } } catch (ex) { return false } } } function unbindIconsClick (icons) { icons.unbind('click') } function setupTouchPlayer (player) { if ($('.touch-video-box').length !== 0) { return } $('.bilibili-player-video-subtitle').before(/* html */`