在观看有关代码的视频时,如果字太小,使用该脚本可以临时放大特定区域便于查看。它并没有调整清晰度,只是放大区域 —— 类似放大镜的功能。
总结:只有在看代码的时候有用,比较鸡肋。
基本使用:
- 按住鼠标中键往右下角拖动,会放大所框选的区域,视频也会继续播放。
- 按住鼠标中键往左上角拖动、或者鼠标中键双击,会还原视频。
注意事项:
- 已经放大的区域无法继续放大。
- 鼠标按住之后,只能右下角、左上角两个移动方式。
- 底部区域无法放大,因为被进度条挡住了。
- 可能和一些鼠标手势的插件冲突。
- 因为框选区域太靠边,可能出现黑框,问题不大。
效果演示:
代码
(function () {
'use strict';
const video = document.querySelector('video');
if (video) { zoomer(video); }
const MaxScale = 10;
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';
}
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 };
}
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);
const left = startX + width / 2;
const top = startY + height / 2;
const { x: elemLeft, y: elemTop } = get_elem_center(elem);
const translateX = elemLeft - left;
const translateY = elemTop - top;
elem.style.transformOrigin = `${left - elem.offsetLeft}px, ${top - elem.offsetTop}px`;
elem.style.transform = `scale(${scale}) translate(${translateX}px, ${translateY}px)`;
}
function zoom_reset(elem) {
elem.style.transformOrigin = '';
elem.style.transform = '';
}
function bind_events(elem) {
let isMouseDown = false;
let startX = 0;
let startY = 0;
let endX = 0;
let endY = 0;
let isZoomed = false;
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;
selectedRect.style.left = `${startX}px`;
selectedRect.style.top = `${startY}px`;
selectedRect.style.display = 'block';
});
elem.addEventListener('mousemove', (e) => {
if (!isMouseDown) { return; }
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";
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();
});
}
bind_events(elem);
}
})();
原理阐述
核心是利用 transform
属性进行多个变换:
- 计算出放大的比例,利用
scale
进行缩放变换。
- 利用
translate
平移变化,将所框选的区域移动到视频所在区域的中心位置。
<video>
元素所在的父元素设置 overflow: hidden
—— 至少 B 站视频不需要考虑这个。
图示说明: