好友
阅读权限25
听众
最后登录1970-1-1
|
本帖最后由 LoveCode 于 2024-11-14 14:43 编辑
在观看有关代码的视频时,如果字太小,使用该脚本可以临时放大特定区域便于查看。它并没有调整清晰度,只是放大区域 —— 类似放大镜的功能。
总结:只有在看代码的时候有用,比较鸡肋。
基本使用:
- 按住鼠标中键往右下角拖动,会放大所框选的区域,视频也会继续播放。
- 按住鼠标中键往左上角拖动、或者鼠标中键双击,会还原视频。
注意事项:
- 已经放大的区域无法继续放大。
- 鼠标按住之后,只能右下角、左上角两个移动方式。
- 底部区域无法放大,因为被进度条挡住了。
- 可能和一些鼠标手势的插件冲突。
- 因为框选区域太靠边,可能出现黑框,问题不大。
效果演示:
代码
// ==UserScript==
// @name B 站视频局部区域放大
// @namespace http://tampermonkey.net/
// @version 0.0.1
// @description 在 B 站观看视频时,可以局部放大视频区域,以便观看细节。
// @AuThor You
// @license MIT
// @match https://www.bilibili.com/video/*
// @Icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @grant none
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const video = document.querySelector('video');
if (video) { zoomer(video); }
/** 最大放大倍数 */
const MaxScale = 10;
/** 为 elem 元素实现鼠标绘制区域的放大功能 */
function zoomer(elem) {
/** 记录鼠标框选区域的元素 */
let selectedRect = null;
/** 初始化显示的矩形框 */
function init_highlight_rect() {
if (selectedRect) { return; }
selectedRect = document.createElement('div');
selectedRect.id = 'highlight-rect';
selectedRect.style.padding = '0';
selectedRect.style.margin = '0';
selectedRect.style.position = 'absolute';
selectedRect.style.border = '2px solid red';
selectedRect.style.backgroundColor = 'transparent';
selectedRect.style.pointerEvents = 'none';
selectedRect.style.zIndex = 99999999;
selectedRect.style.display = 'none';
document.body.appendChild(selectedRect);
}
/** 重置显示的矩形框 */
function reset_highlight_rect() {
if (!selectedRect) { return; }
selectedRect.style.display = 'none';
selectedRect.style.width = '0';
selectedRect.style.height = '0';
}
/** 获取元素 elem 的中心位置,基于 Page 页面坐标 */
function get_elem_center(elem) {
// 先获取元素基于视口的位置
const rect = elem.getBoundingClientRect();
// 然后计算元素的中心位置,基于视口的
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
// 然后加上滚动条的偏移量,得到基于页面的坐标
const pageX = centerX + document.documentElement.scrollLeft;
const pageY = centerY + document.documentElement.scrollTop;
return { x: pageX, y: pageY };
}
/** 放大元素的某个区域,这里的 startX、startY 是基于 Page 页面的 */
function zoom_in(elem, startX, startY, width, height) {
// 计算缩放比列
const scaleX = elem.offsetWidth / width;
const scaleY = elem.offsetHeight / height;
const scale = Math.min(Math.min(scaleX, scaleY).toFixed(2), MaxScale);
// 获取要放大区域的中心位置,基于 Page 页面的
const left = startX + width / 2;
const top = startY + height / 2;
// 计算 elem 元素的中心,也是基于 Page 页面的
const { x: elemLeft, y: elemTop } = get_elem_center(elem);
// 计算把该区域中心平移到 elem 元素中心的距离
const translateX = elemLeft - left;
const translateY = elemTop - top;
// 设置元素的 transform-origin 属性,使其缩放的中心点在矩形框的中心位置
elem.style.transformOrigin = `${left - elem.offsetLeft}px, ${top - elem.offsetTop}px`;
// 然后,设置元素的 transform 属性,先缩放,再平移
elem.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;
}
/** 重置元素的缩放 */
function zoom_reset(elem) {
elem.style.transformOrigin = '';
elem.style.transform = '';
}
/** 为元素 elem 绑定鼠标事。
* - 当鼠标中键被按下、移动时可以显示矩形框
* - 当鼠标中键松开时,可以放大或缩小元素
*/
function bind_events(elem) {
let isMouseDown = false;
// 记录鼠标按下时的坐标,基于页面
let startX = 0;
let startY = 0;
// 记录鼠标抬起时的坐标,基于页面
let endX = 0;
let endY = 0;
/** 记录是否已经缩放过 */
let isZoomed = false;
/** 为 "" 表示还没有进行任何鼠标操作。
*
* 为 "zomin" 表示鼠标往右下方向移动,需要放大图片大小
*
* 为 "reset" 表示鼠标往左上方向移动,需要还原图片大小
*/
let operation = "";
init_highlight_rect();
// 鼠标中键快速双击也能取消缩放
let lastClickTime = Date.now();
elem.addEventListener('mousedown', (e) => {
// 只有鼠标中键点击才行
if (e.button !== 1) { return; }
// 防止默认行为 —— 出现向下的滚动
e.preventDefault();
// 判断是否为双击
const now = Date.now();
if (now - lastClickTime < 300) {
zoom_reset(elem);
isZoomed = false;
isMouseDown = false;
operation = "";
reset_highlight_rect();
return;
}
lastClickTime = now;
isMouseDown = true;
// 基于页面的坐标
startX = e.pageX;
startY = e.pageY;
// 设置矩形框的起点,注意了,这个起点坐标是基于 Page 的
selectedRect.style.left = `${startX}px`;
selectedRect.style.top = `${startY}px`;
selectedRect.style.display = 'block';
});
elem.addEventListener('mousemove', (e) => {
if (!isMouseDown) { return; }
// 计算鼠标移动的距离,基于 Page 坐标系
endX = e.pageX;
endY = e.pageY;
const x = endX - startX;
const y = endY - startY;
// 鼠标是往右下方向移动,需要放大图片大小
// 如果已经放大了,那就不能再放大了
if (!isZoomed && x > 0 && y > 0) {
if (operation !== "zoomin") {
operation = "zoomin";
selectedRect.style.transformOrigin = '';
selectedRect.style.transform = '';
}
}
// 鼠标是往左上方向移动
else if (x < 0 && y < 0) {
if (operation !== "reset") {
operation = "reset";
// rect 绕着起使位置旋转 180 度
selectedRect.style.transformOrigin = 'left top';
selectedRect.style.transform = 'rotate(180deg)';
}
}
// 其它情况不进行处理
else { }
// 然后绘制矩形框的宽高
if (operation !== "") {
selectedRect.style.width = `${Math.abs(x)}px`;
selectedRect.style.height = `${Math.abs(y)}px`;
}
});
// 鼠标松开时,隐藏矩形框,然后放大或缩小元素
elem.addEventListener('mouseup', (e) => {
if (!isMouseDown) { return; }
isMouseDown = false;
reset_highlight_rect();
if (operation === "zoomin") {
zoom_in(elem, startX, startY,
Math.abs(endX - startX), Math.abs(endY - startY));
isZoomed = true;
}
else if (operation === "reset") {
zoom_reset(elem);
isZoomed = false;
}
operation = "";
});
// 鼠标移出元素时,隐藏矩形框,然后什么都不做
elem.addEventListener('mouseout', () => {
isMouseDown = false;
operation = "";
reset_highlight_rect();
});
}
/** main code */
bind_events(elem);
}
})();
原理阐述
核心是利用 transform 属性进行多个变换:
- 计算出放大的比例,利用
scale 进行缩放变换。
- 利用
translate 平移变化,将所框选的区域移动到视频所在区域的中心位置。
<video> 元素所在的父元素设置 overflow: hidden —— 至少 B 站视频不需要考虑这个。
图示说明:
|
免费评分
-
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|