旋转和缩放视频,防止某些视频伤害到你的脖子或眼睛!
// ==UserScript== // @name [Bilibili] 视频旋转 // @namespace ckylin-script-bilibili-rotate // @version 0.15 // @description 旋转和缩放视频,防止某些视频伤害到你的脖子或眼睛! // @author CKylinMC // @match https://www.bilibili.com/video/* // @include https://www.bilibili.com/medialist/play/* // @include https://www.bilibili.com/bangumi/play/* // @include https://bangumi.bilibili.com/anime/*/play* // @include https://bangumi.bilibili.com/movie/* // @grant GM_registerMenuCommand // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_getResourceText // @grant unsafeWindow // @license GPL-3.0-only // ==/UserScript== (function () { 'use strict'; let effects = []; /* * 实验性功能: * `injectToVideo`开关决定将面板注入到页面外围还是播放器中。 * * 如果设置为 true 脚本会注入到播放器中,那么你可以在全屏时使用面板,但是由于b站 * 切换视频会重置播放器,因此脚本将会持续观测播放器以响应变更 * 此功能可能会导致页面卡顿或其他问题。 * * 如果设置为 false 脚本会注入到页面外围,此时脚本注入完成后不会影响页面功能, * 但是如果你进入全屏模式将无法看到侧功能栏,脚本将只能通过快捷键来操作视频旋转缩放。 * * 此选项由脚本自动维护,无需手动修改。 */ let injectToVideo = false; const loadInjectOption = (val=null) =>{ if(val!==null){ GM_setValue('ckrotate_injecttovideo',val); } let opt = GM_getValue('ckrotate_injecttovideo',false); if(opt===null||opt===undefined) opt = false; injectToVideo = opt; } loadInjectOption(); const wait = (t) => { return new Promise(r => setTimeout(r, t)); } const waitForPageVisible = async () => { return document.hidden && new Promise(r=>document.addEventListener("visibilitychange",r)) } class EventEmitter { handlers = {}; on(name, func) { if (!(func instanceof Function)) throw "Param must be func!"; if (!(name in this.handlers)) { this.handlers[name] = []; } this.handlers[name].push(func); } off(name, func) { if (!(func instanceof Function)) throw "Param must be func!"; if (name in this.handlers) { for (let i = 0; i < this.handlers[name].length; i++) { if (this.handlers[name][i] === func) { this.handlers[name].splice(i, 1); i--; } } } } emit(name, ...args) { if (name in this.handlers) { for (let func of this.handlers[name]) { try { func(...args); } catch (e) { console.error('ERROR:', e); } } } } } class HoldClick { dom; emitter = new EventEmitter; downTime = 0; holdingTime = 250; mouseDown = false; constructor(dom, holdingTime = 250) { if (dom instanceof HTMLElement) { this.dom = dom; this.initListener(); } this.holdingTime = holdingTime; } onclick(func) { this.emitter.on("click", func); return this; } onhold(func) { this.emitter.on("hold", func); return this; } onup(func) { this.emitter.on("up", func); return this; } offclick(func) { this.emitter.off("click", func); return this; } offhold(func) { this.emitter.off("hold", func); return this; } offup(func) { this.emitter.off("up", func); return this; } handleMouseDown(e) { e.preventDefault(); this.mouseDown = true; this.downTime = (new Date()).getTime(); setTimeout(() => { if (this.mouseDown) { console.log(this); this.mouseDown = false; this.downTime = 0; this.emitter.emit("hold", e); } }, this.holdingTime) } handleMouseUp(e) { e.preventDefault(); if (this.mouseDown) { this.mouseDown = false; const currTime = (new Date).getTime(); if ((currTime - this.downTime) >= this.holdingTime) { this.emitter.emit("hold", e); } else { this.emitter.emit("click", e); } this.downTime = 0; } this.emitter.emit("up", e); } handleMouseOut(e) { e.preventDefault(); if (this.mouseDown) { this.mouseDown = false; this.downTime = 0; this.emitter.emit("hold", e); } } initListener() { this.dom.addEventListener("mouseup", this.handleMouseUp.bind(this)) this.dom.addEventListener("mousedown", this.handleMouseDown.bind(this)) this.dom.addEventListener("mouseout", this.handleMouseOut.bind(this)) } } const dragger = { defaultHandler: (val) => console.log("DRAG:", val), waitForDragger: async (waitStatus = true) => { while (dragger.dragging !== waitStatus) await wait(10); return dragger; }, regHandler: async (func) => { if (!(func instanceof Function)) throw "Param must be a func!"; await dragger.waitForDragger(false); dragger.handler = func; return dragger; }, handler: () => { }, dragging: false, initialDragData: { x: 0, y: 0 }, lastDragData: { x: 0, y: 0 }, startDrag: (e) => { if (dragger.dragging) return; dragger.dragging = true; console.log(dragger.initialDragData); dragger.initialDragData.x = e.screenX; dragger.initialDragData.y = e.screenY; dragger.lastDragData.x = e.screenX; dragger.lastDragData.y = e.screenY; document.body.addEventListener("mouseup", dragger.stopDrag); document.body.addEventListener("mousemove", dragger.handleDrag); console.info("DRAG:", "Start Drag"); return dragger; }, handleDrag: (e) => { const currPos = { x: e.screenX, y: e.screenY }; const initPos = dragger.initialDragData; const delta = { x: initPos.x - currPos.x, y: initPos.y - currPos.y } const lastdelta = { x: dragger.lastDragData.x - currPos.x, y: dragger.lastDragData.y - currPos.y } dragger.lastDragData = currPos; dragger.handler(delta, lastdelta); }, stopDrag: () => { document.body.removeEventListener("mouseup", dragger.stopDrag); document.body.removeEventListener("mousemove", dragger.handleDrag); dragger.handler = dragger.defaultHandler; console.info("DRAG:", "Stop Drag"); dragger.dragging = false; return dragger; }, } async function playerReady() { let i = 50; while (--i >= 0) { await wait(100); if (!('player' in unsafeWindow)) continue; if (!('isInitialized' in unsafeWindow.player)) continue; if (!unsafeWindow.player.isInitialized()) continue; } if(i<0)return false; await waitForPageVisible(); while(1){ await wait(200); if(document.querySelector(".bilibili-player-video-control-wrap,.bpx-player-video-wrap")) return true; } } function bindKeys() { unsafeWindow.addEventListener("keypress", e => { if (e.key == "Q") { if (!e.shiftKey) return; if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return; leftR(); e.preventDefault(); } else if (e.key == "E") { if (!e.shiftKey) return; if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return; rightR(); e.preventDefault(); } else if (e.key == "A") { if (!e.shiftKey) return; if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return; smartLR(); e.preventDefault(); } else if (e.key == "D") { if (!e.shiftKey) return; if (["INPUT", "TEXTARREA"].includes(e.target.tagName)) return; smartRR(); e.preventDefault(); } else if (e.key == "R") { if (!e.shiftKey) return; if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return; cleanEffects(); clearStyles(); e.preventDefault(); } else if (e.key == "+") { if (!e.shiftKey) return; if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return; zoomIn(); e.preventDefault(); } else if (e.key == "-") { if (!e.shiftKey) return; if (["INPUT", "TEXTAREA"].includes(e.target.tagName)) return; zoomOut(); e.preventDefault(); } }); } function addEffects(name, value, wait = false) { let effect = effects.filter(e => e.name == name); if (effect.length) { effect[0].value += value; } else { effects.push({name, value}); } if (!wait) applyEffects(); } function setEffects(name, value, wait = false) { delEffect(name, true); addEffects(name, value, wait); } function delEffect(name, wait = false) { effects.forEach((e, i) => { if (e.name == name) effects.splice(i, 1); }) if (!wait) applyEffects(); } function applyEffects() { let style = ".bilibili-player-video video,.bilibili-player-video bwp-video,.bpx-player-video-wrap video { transform: "; effects.forEach(e => { let key = e.name; let value = e.value + ""; switch (key) { case "rotate": value += "deg"; break; case "translateY": case "translateX": value += "px"; break; case "scale": value = (1 - e.value) + ""; break; } style += ` ${key}(${value})`; }); style += "}"; console.log(style); clearStyles(); addStyle(style); } function cleanEffects() { effects = []; } function clearStyles(className = "CKROTATE") { let dom = document.querySelectorAll("style." + className); if (dom) [...dom].forEach(e => e.remove()); } function addStyle(s, className = "CKROTATE") { if (className !== "CKROTATE") clearStyles(className); let style = document.createElement("style"); style.classList.add(className); style.innerHTML = s; document.body.appendChild(style); } function setR(val = 0) { setEffects("rotate", val); } function leftR(val = 90) { addEffects("rotate", val * -1); } function rightR(val = 90) { //debug //alert("rightR"); addEffects("rotate", val); } function upR() { addEffects("rotate", 180); } function cR() { delEffect("rotate"); } function setZ(val = 0) { setEffects("scale", val); } function zoomIn(val = 0.1) { addEffects("scale", val * -1); } function zoomOut(val = 0.1) { addEffects("scale", val); } function cZ() { delEffect("scale"); } function setMY(val = 0) { setEffects("translateY", val); } function moveUp(val = 10) { addEffects("translateY", val * -1); } function moveDown(val = 10) { addEffects("translateY", val); } function setMX(val = 0) { setEffects("translateX", val); } function setPos(x, y) { setEffects("translateX", x, true); setEffects("translateY", y); } function movePos(x, y) { addEffects("translateX", x, true); addEffects("translateY", y); } function moveLeft(val = 10) { addEffects("translateX", val * -1); } function moveRight(val = 10) { addEffects("translateX", val); } function cM() { delEffect("translateX"); delEffect("translateY"); } function smartLR() { let dom = document.querySelector(".bilibili-player-video video,.bilibili-player-video bwp-video,.bpx-player-video-wrap video"); if (!dom) return; let w = dom.videoWidth; let h = dom.videoHeight; let s = h / w; clearStyles(); cleanEffects(); addEffects("rotate", -90, true); addEffects("scale", 1 - s); } function smartRR() { let dom = document.querySelector(".bilibili-player-video video,.bilibili-player-video bwp-video,.bpx-player-video-wrap video"); if (!dom) return; let w = dom.videoWidth; let h = dom.videoHeight; let s = h / w; clearStyles(); cleanEffects(); addEffects("rotate", 90, true); addEffects("scale", 1 - s); } function showTip() { addStyle(` #CKToast{ background: white; position: fixed; top: 80px; right: 20px; border-radius: 3px; border-left: solid 4px #2196f3; padding: 10px; color: black; font-size: large; overflow: hidden; word-break: all; animation: CKToastIn cubic-bezier(0, 0, 0, 1.18) .5s forwards; } .dark #CKToast{ background: #424242; color: white; } #CKToast button{ border: none; background: #2196f3; color:white; padding:3px 6px; display: inline-block; margin: 3px; border-radius: 3px; cursor:pointer; font-size: medium; transition: all .3s; } #CKToast button:hover{ filter: brightness(.5); } .dark #CKToast button{ background: #1976d2; } @keyframes CKToastIn{ from{ right: -100%; } } @keyframes CKToastOut{ to{ right: -100%; } } `, "CKToastUIStyles"); const toast = document.createElement("div"); toast.id = "CKToast"; toast.innerHTML = "检测到视频可能需要旋转<br>"; const left = document.createElement("button"); left.innerHTML = "左转90°"; left.onclick = () => { smartLR(); closeTip(); } toast.appendChild(left); const right = document.createElement("button"); right.innerHTML = "右转90°"; right.onclick = () => { smartRR(); closeTip(); } toast.appendChild(right); const close = document.createElement("button"); close.innerHTML = "关闭"; close.style.background = "#d81b60"; close.onclick = () => { closeTip(); } toast.appendChild(close); document.body.appendChild(toast); setTimeout(closeTip, 10000); } function closeTip() { const toast = document.querySelector("#CKToast"); if (toast) { toast.style.animation = null; toast.style.animation = "CKToastOut cubic-bezier(0.93, -0.32, 1, 1) .5s forwards"; setTimeout(() => toast.remove(), 500); } } async function videoDetect() { if (!(await playerReady())) return; let dom = document.querySelector(".bilibili-player-video video,.bilibili-player-video bwp-video,.bpx-player-video-wrap video"); if (!dom) return; let w = dom.videoWidth; let h = dom.videoHeight; if (h > w) { showTip(); } } GM_registerMenuCommand("左转90", () => { leftR(); }); GM_registerMenuCommand("右转90°", () => { rightR(); }); GM_registerMenuCommand("智能左转90", () => { smartLR(); }); GM_registerMenuCommand("智能右转90°", () => { smartRR(); }); GM_registerMenuCommand("180°", () => { upR(); }); GM_registerMenuCommand("放大", () => { zoomIn(); }); GM_registerMenuCommand("缩小°", () => { zoomOut(); }); GM_registerMenuCommand("向上", () => { moveUp(); }); GM_registerMenuCommand("向下", () => { moveDown(); }); GM_registerMenuCommand("向左", () => { moveLeft(); }); GM_registerMenuCommand("向右", () => { moveRight(); }); GM_registerMenuCommand("清除旋转", () => { cR(); }); GM_registerMenuCommand("清除缩放", () => { cZ(); }); GM_registerMenuCommand("清除位移", () => { cM(); }); GM_registerMenuCommand("重置", () => { cleanEffects(); clearStyles(); }); GM_registerMenuCommand("注入播放器", () => { if(confirm(`注入播放器功能(实验性) * 当前状态:${injectToVideo?"开":"关"} * 点击"确认"修改为 ${injectToVideo?"关":"开"} ,点击取消不做修改。 * * 说明 * * 此开关决定将面板注入到页面外围还是播放器中。 * * 如果设置为 开 脚本会注入到播放器中,那么你可以在全屏时使用面板,但是由于b站 * 切换视频会重置播放器,因此脚本将会持续观测播放器以响应变更。 * 此功能可能会导致页面卡顿或其他问题(欢迎反馈遇到的问题)。 * * 如果设置为 关 脚本会注入到页面外围,此时脚本注入完成后不会影响页面功能, * 但是如果你进入全屏模式将无法看到侧功能栏,脚本将只能通过快捷键来操作视频旋转缩放。`)){ loadInjectOption(!injectToVideo); alert(`已修改为 ${injectToVideo?"开":"关"} 刷新页面生效`); } }); /* Thanks for yoringboy's contributings! */ function makeButton(icon, contents, color) { let ico = document.createElement("i"); ico.classList.add("mdi", "mdi-18px", "mdi-" + icon); if (color) ico.style.color = color; let btn = document.createElement("div"); btn.classList.add("ckrotate-btn"); // btn.innerHTML = contents; btn.appendChild(ico); btn.setAttribute("data-btnname", contents); return btn } let lastObTimeout = null; let ignoreNextObEvent = false; let ob = null function injectButtons() { if(!document.querySelector("#mdiiconcss")) document.head.innerHTML += `<link id="mdiiconcss" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css"/>` addStyle(` #ckrotate-hidden-btn{ z-index: 9999; position: fixed; left: -15px; width: 30px; height: 30px; background: black; opacity: 0.1; color: white; cursor: pointer; border-radius: 50%; text-align: right; line-height: 30px; transition: opacity .3s 1s, background .3s, color .3s; top: 120px; top: 30vh; } #ckrotate-hidden-btn:hover{ transition: opacity .3s 0s, background .3s, color .3s; background: white; color: black; opacity: 0.75; } #ckrotate-hidden-btn.hide{ left: -40px; } #ckrotate-btn-base{ z-index: 9999; position: fixed; top: 55px; left: 20px; width: 110px; height: 450px; opacity: 0.75; background: black; color: white; text-align: center; cursor: pointer; flex: 1; border-radius: 8px; overflow: hidden; display: flex; flex-direction: column; flex-wrap: wrap; align-content: stretch; justify-content: space-between; align-items: center; max-height: 90vh; transition: all .3s; } #ckrotate-btn-base.hide{ left: -120px; opacity: 0; } #ckrotate-btn-base .ckrotate-btn{ display: flex; width: 55px; flex-flow: column; min-height: 55px; flex-wrap: nowrap; align-content: center; justify-content: center; align-items: center; transition: all .3s; border-radius: 8px; background: black; } #ckrotate-btn-base .ckrotate-btn:hover{ background: white; color: transparent; } #ckrotate-btn-base .ckrotate-btn:hover>*{ opacity: 0; } #ckrotate-btn-base .ckrotate-btn:hover::after{ color: black; content: attr(data-btnname); transform: translateY(-80%); } `, "CKRotateBtnsStyle"); const togglePanel = show => { const btn = document.querySelector("#ckrotate-hidden-btn"); const panel = document.querySelector("#ckrotate-btn-base"); if (show) { btn.className = "hide"; panel.className = "show"; } else { btn.className = "show"; panel.className = "hide"; } }; const toggle = document.createElement("div"); toggle.id = "ckrotate-hidden-btn"; toggle.innerHTML = `<i class="mdi mdi-18px mdi-chevron-right"></i>`; toggle.onclick = () => togglePanel(true); const btnRoot = document.createElement("div"); btnRoot.id = "ckrotate-btn-base"; btnRoot.classList.add("hide"); let toggleBtn = makeButton("chevron-left", "隐藏"); toggleBtn.onclick = () => togglePanel(false); btnRoot.appendChild(toggleBtn); let LRBtn = makeButton("rotate-left", "左转90", "orange"); new HoldClick(LRBtn) .onclick(() => { leftR(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,false,{x:e.clientX,y:e.clientY},{down:"rotate-right",up:"rotate-left"}); await (await dragger.regHandler((delta, lastdelta) => leftR(lastdelta.y))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // LRBtn.onclick = function () { // leftR(); // }; // LRBtn.oncontextmenu = async function (e){ // e.preventDefault(); // disableAnim(); // await (await dragger.regHandler(delta => { // setR(delta.y*-1); // })).startDrag(e); // enableAnim(); // }; btnRoot.appendChild(LRBtn); let RRBtn = makeButton("rotate-right", "右转90", "orange"); new HoldClick(RRBtn) .onclick(() => { rightR(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,false,{x:e.clientX,y:e.clientY},{up:"rotate-right",down:"rotate-left"}); await (await dragger.regHandler((delta, lastdelta) => rightR(lastdelta.y))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // RRBtn.onclick = function () { // rightR(); // }; // RRBtn.oncontextmenu = async function (e){ // e.preventDefault(); // disableAnim(); // await (await dragger.regHandler(delta => { // setR(delta.y); // })).startDrag(e); // enableAnim(); // }; btnRoot.appendChild(RRBtn); let RVBtn = makeButton("rotate-3d-variant", "翻转", "orange"); RVBtn.onclick = function () { upR(); }; btnRoot.appendChild(RVBtn); let SLRBtn = makeButton("undo", "智能左转", "yellow"); SLRBtn.onclick = function () { smartLR(); }; btnRoot.appendChild(SLRBtn); let SRRBtn = makeButton("redo", "智能右转", "yellow"); SRRBtn.onclick = function () { smartRR(); }; btnRoot.appendChild(SRRBtn); let ZIBtn = makeButton("arrow-expand-all", "放大", "cadetblue"); new HoldClick(ZIBtn) .onclick(() => { zoomIn(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,false,{x:e.clientX,y:e.clientY},{up:"arrow-expand-all",down:"arrow-collapse-all"}); await (await dragger.regHandler((delta, lastdelta) => zoomIn(lastdelta.y * 0.01))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // ZOBtn.onclick = function () { // zoomIn(); // }; btnRoot.appendChild(ZIBtn); let ZOBtn = makeButton("arrow-collapse-all", "缩小", "cadetblue"); new HoldClick(ZOBtn) .onclick(() => { zoomOut(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,false,{x:e.clientX,y:e.clientY},{down:"arrow-expand-all",up:"arrow-collapse-all"}); await (await dragger.regHandler((delta, lastdelta) => zoomOut(lastdelta.y * 0.01))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // ZIBtn.onclick = function () { // zoomOut(); // }; btnRoot.appendChild(ZOBtn); let MUBtn = makeButton("pan-up", "上移", "forestgreen"); new HoldClick(MUBtn) .onclick(() => { moveUp(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,true,{x:e.clientX,y:e.clientY}); await (await dragger.regHandler((delta, lastdelta) => movePos(lastdelta.x*-1, lastdelta.y*-1))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // MUBtn.onclick = function () { // moveUp(); // }; btnRoot.appendChild(MUBtn); let MDBtn = makeButton("pan-down", "下移", "forestgreen"); new HoldClick(MDBtn) .onclick(() => { moveDown(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,true,{x:e.clientX,y:e.clientY}); await (await dragger.regHandler((delta, lastdelta) => movePos(lastdelta.x*-1, lastdelta.y*-1))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // MDBtn.onclick = function () { // moveDown(); // }; btnRoot.appendChild(MDBtn); let MLBtn = makeButton("pan-left", "左移", "forestgreen"); new HoldClick(MLBtn) .onclick(() => { moveLeft(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,true,{x:e.clientX,y:e.clientY}); await (await dragger.regHandler((delta, lastdelta) => movePos(lastdelta.x*-1, lastdelta.y*-1))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // MLBtn.onclick = function () { // moveLeft(); // }; btnRoot.appendChild(MLBtn); let MRBtn = makeButton("pan-right", "右移", "forestgreen"); new HoldClick(MRBtn) .onclick(() => { moveRight(); }) .onhold(async (e) => { disableAnim(); showArrows(btnRoot,true,{x:e.clientX,y:e.clientY}); await (await dragger.regHandler((delta, lastdelta) => movePos(lastdelta.x*-1, lastdelta.y*-1))).startDrag(e).waitForDragger(false).then(() => {enableAnim();hideArrows()}); }) // MRBtn.onclick = function () { // moveRight(); // }; btnRoot.appendChild(MRBtn); let CRBtn = makeButton("backup-restore", "清除旋转", "orange"); CRBtn.onclick = function () { cR(); }; btnRoot.appendChild(CRBtn); let CZBtn = makeButton("magnify-remove-outline", "清除缩放", "cadetblue"); CZBtn.onclick = function () { cZ(); }; btnRoot.appendChild(CZBtn); let CMBtn = makeButton("pan", "清除位移", "forestgreen"); CMBtn.onclick = function () { cM(); }; btnRoot.appendChild(CMBtn); let RSBtn = makeButton("close-circle-outline", "重置", "orangered"); RSBtn.onclick = function () { cleanEffects(); clearStyles(); }; btnRoot.appendChild(RSBtn); let injectBase; ignoreNextObEvent = true; if(injectToVideo){ injectBase = document.querySelector("#bilibiliPlayer") || document.body; if(ob===null){ ob = new MutationObserver(()=>{ if(ignoreNextObEvent) return; if(lastObTimeout) clearTimeout(lastObTimeout); setTimeout(()=>{ injectButtons(); lastObTimeout = null; },300); }); } ob.observe(document.querySelector("#bilibiliPlayer"),{childList:true}); }else{ injectBase = document.body; } injectBase.appendChild(toggle); injectBase.appendChild(btnRoot); ignoreNextObEvent = false; } function showArrows(domRoot = document.body, horizontal=false, pos = {x:0,y:0}, iconlist = {}){ let icons = { icon:iconlist.icon||"hand-right", up:iconlist.up||"arrow-up", down:iconlist.down||"arrow-down", left:iconlist.left||"arrow-left", right:iconlist.right||"arrow-right" } addStyle(` #CKROTATE-arrows-base{ position:fixed; pointer-events: none; width: 60px; height: 60px; background: #00a1d6; box-shadow: 0px 0px 8px #00a1d6; transform: translate(-50%,-50%); text-align: center; border-radius: 50%; border: solid 3px #00a1d6; } .CKROTATE-arrow-item{ padding:0; margin: 0 auto; line-height: 20px; /*height: 20px;*/ color: white; } `,"CKROTATE-UI-ARROWS"); hideArrows(); const baseDom = document.createElement("div"); baseDom.id="CKROTATE-arrows-base"; baseDom.style.top = pos.y+"px"; baseDom.style.left = pos.x+"px"; const UpArrow = document.createElement("div"); UpArrow.classList.add("CKROTATE-arrow-item"); UpArrow.innerHTML = `<i class="mdi mdi-18px mdi-${icons.up}"></i>`; baseDom.appendChild(UpArrow); const LRArrow = document.createElement("div"); LRArrow.classList.add("CKROTATE-arrow-item"); if(horizontal){ const LeftArrow = document.createElement("div"); LeftArrow.classList.add("CKROTATE-arrow-item"); LeftArrow.innerHTML = `<i class="mdi mdi-18px mdi-${icons.left}"></i>`; LeftArrow.style.float="left"; LRArrow.appendChild(LeftArrow); const RightArrow = document.createElement("div"); RightArrow.classList.add("CKROTATE-arrow-item"); RightArrow.innerHTML = `<i class="mdi mdi-18px mdi-${icons.right}"></i>`; RightArrow.style.float="right"; LRArrow.appendChild(RightArrow); }else{ LRArrow.innerHTML = `<i class="mdi mdi-18px"></i>`; } baseDom.appendChild(LRArrow); const CenterPoint = document.createElement("div"); CenterPoint.classList.add("CKROTATE-arrow-item"); CenterPoint.innerHTML = `<i class="mdi mdi-18px mdi-${icons.icon}"></i>`; baseDom.appendChild(CenterPoint); const DownArrow = document.createElement("div"); DownArrow.classList.add("CKROTATE-arrow-item"); DownArrow.innerHTML = `<i class="mdi mdi-18px mdi-${icons.down}"></i>`; baseDom.appendChild(DownArrow); domRoot.appendChild(baseDom); } function hideArrows(){ let olddom = document.querySelector("#CKROTATE-arrows-base"); if(olddom) olddom.remove(); } function enableAnim() { addStyle(".bilibili-player-video video,.bilibili-player-video bwp-video,.bpx-player-video-wrap video{transition: transform cubic-bezier(0.61, 0.01, 0.44, 0.93) .5s;}", "CKANIMATION"); } function disableAnim() { clearStyles("CKANIMATION"); } async function startInject() { enableAnim(); bindKeys(); await videoDetect(); injectButtons(); } startInject() .then(()=>console.log("[CKROTATE]","Inject task completed.")) .catch(e=>console.error("[CKROTATE]","Inject task failed:",e)); })();