吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 676|回复: 31
上一主题 下一主题
收起左侧

[其他原创] B站网页版视频区域放大

[复制链接]
跳转到指定楼层
楼主
LoveCode 发表于 2024-11-14 14:31 回帖奖励
本帖最后由 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 属性进行多个变换:

  1. 计算出放大的比例,利用 scale 进行缩放变换。
  2. 利用 translate 平移变化,将所框选的区域移动到视频所在区域的中心位置。
  3. <video> 元素所在的父元素设置 overflow: hidden —— 至少 B 站视频不需要考虑这个。

图示说明:

免费评分

参与人数 6威望 +1 吾爱币 +14 热心值 +5 收起 理由
苏紫方璇 + 1 + 10 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wo8962892 + 1 我很赞同!
wangdanq + 1 + 1 谢谢@Thanks!
Penko + 1 我很赞同!感谢分享
片地花开 + 1 + 1 我很赞同!
天才笨蜀黍 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
冬天冷了多穿点 发表于 2024-11-14 14:49
感谢分享
3#
Do_zh 发表于 2024-11-14 14:50
4#
nokia790 发表于 2024-11-14 14:57
5#
cliffsu 发表于 2024-11-14 14:58
厉害,很实用
6#
kangta520 发表于 2024-11-14 15:09
执行不了?中键还是原有上下转功能
7#
猫之茗 发表于 2024-11-14 15:11
感谢分享,晚上去试试。
8#
natsuki777 发表于 2024-11-14 15:17
感谢分享
9#
SN1t2lO 发表于 2024-11-14 15:37
系统自带的放大镜算是要下岗了
10#
lerd 发表于 2024-11-14 15:38
感觉很实用啊,待会试一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 09:26

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表