登录 立即注册

首页 > 绿虎论坛 > 网页插件 (发帖)

标题: 【网页插件】图片点击加载(测试版 r8)

作者: @Ta

时间: 2022-07-28发布,2022-07-31修改

点击: 108505

自动更新代码(推荐使用):

导入网页插件:图片点击加载(自动更新)(当前用户:14,总安装次数:24)
<script src="https://hu60.cn/q.php/api.webplug-file.1_public_img_click_load.js"></script>

如果你有多个插件,请将图片点击加载插件移到插件列表的开头,以达到更好的流量节省效果。

如果图片点击加载插件位于其他插件之后,可能会导致部分图片在插件生效前就加载完成,节省流量的目的就不能完全实现。


完整代码仅供参考,不能直接执行

(() => {
    function getSizeFromUrl(url) {
        url = hu60_decode_url64(url);
        var parts = url.match(/\/file\/(?:hash\/[^\/]+\/[a-f0-9]{32}|uuid\/[^\/]+\/[a-f0-9-]{36})(\d+)\b/);
        return parts ? parseInt(parts[1]) : 0;
    }
    function getImgSize(img) {
        if (img.alt) {
            var parts = img.alt.match(/([0-9]+(:?\.[0-9]+)?\s*(?:[KMGT]B|bytes))/);
            if (parts) return parts[1];
        }
        var size = getSizeFromUrl(img.src);
        return size > 0 ? humanize.filesize(size) : '';
    }
    document.querySelectorAll('.userimg').forEach(x => {
        // 暂时抑制外层链接
        if (x.parentNode.href) {
            x.parentNode._href = x.parentNode.href;
            x.parentNode.href = `javascript:void(0);`;
        }

        // 保存原始URL
        x._url = x.src;

        // 图片替换为文字SVG
        var outLink = x.parentNode.href ? `<text x="15" y="125" fill="red">再次点击打开链接</text>` : '';
        var alt = x.alt ? escapeHtml(x.alt) : '';
        var size = getSizeFromUrl(x.src);
        size = getImgSize(x);
        x.src = `data:image/svg+xml;utf8,
            <svg xmlns="http://www.w3.org/2000/svg" version="1.1">
              <text x="15" y="35" fill="red">${alt}</text>
              <text x="15" y="65" fill="red">${size}</text>
              <text x="15" y="95" fill="red">点击加载图片</text>
              ${outLink}
            </svg>`;
    });
})();
$(document).ready(() => {
    // 加这个延时,就可以保证晚于任何其他插件执行,以解决和viewer.js的冲突
    setTimeout(() => {
        document.querySelectorAll('.userimg').forEach(x => {
            // 替换掉虎绿林的点击查看大图事件
            x._onclick = x.onclick;
            x.onclick = function () {
                this.src = this._url;
                // 再次点击就触发虎绿林的点击查看大图事件
                x.onclick = x._onclick;
                // 恢复被抑制的图片外层链接
                if (x.parentNode._href) {
                    // 为了防止外层链接被立即触发,所以加个延时
                    setTimeout(() => x.parentNode.href = x.parentNode._href);
                }
            };
        });
    }, 10);
});

[隐藏样式|查看源码]


『回复列表(72|隐藏机器人聊天)』

1. 高效
(/@Ta/2022-07-28 09:20//)

2. 这个与之前viewer插件冲突
(/@Ta/2022-07-28 09:29//)

3.

对markdown link+img 无效
[![]()]()

测试帖 https://hu60.cn/q.php/bbs.topic.103447.html
HUAWEI Mate X2(冰晶蓝)兔兔图床

(/@Ta/2022-07-28 14:28//)

4.

@秀才,图片外面有个链接,没办法点击加载。你点击的同时肯定会触发外面那个链接啊。

// 图片外层是链接,做不到点击加载
if (x.parentNode.href) return;
(/@Ta/2022-07-28 14:38//)

5.

@秀才,哦对了,如果只是想实现点击查看大图,现在虎绿林已经支持了,直接写![]()就能点击查看大图,不再需要套一层链接。

(/@Ta/2022-07-28 14:37//)

6.

@秀才,我又修改了一下,实现了对包含外层链接的图片的兼容。现在点击一次加载图片,点击两次打开链接。

(/@Ta/2022-07-28 15:24//)

7.

@老虎会游泳,我改写了下,主要更新内容:

  1. 按钮变得好看些
  2. 自动使图片、音乐、视频、内嵌HTML变成点击显示
  3. 自动检测目标链接文件大小。对于图片,若已缓存或较小(如 < 1MB),则直接显示

感觉还存在以下问题:

  1. 不知道有没有直接获取文件大小的方法。

    目前是用fetchHEAD方法再发送一次请求,解析content-length判定文件大小。

    • 会不会有啥安全问题?
    • 会不会跨域啥的获取不了大小?
    • 会不会哪个服务器把HEADGET看,直接返回整个文件?
  2. 还是和图片浏览器插件冲突。

    按理说,我保留了原始img节点,onclick啥的应该都还保留着?

@老虎会游泳,有空能指教下,问题出哪儿不?

<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<script>

  const blocks = {
    userimg: {type: 'img', icon: 'image'},
    iframe_box: {type: 'iframe', icon: 'web'},
    audio_box: {type: 'audio', icon: 'audiotrack'},
    video_box: {type: 'video', icon: 'movie'},
  };

  function formatSize(size) {
    const i = size && Math.floor(Math.log(size) / Math.log(1024));
    return (size / (1 << (10 * i))).toFixed(i && 2) + ' ' + ' KMGT'[i].trim() + 'B';
  }

  customElements.define('my-button', class extends HTMLElement {
    #ref = {};
    #orig_node;
    #media_src;
    #media_node;

    #createElementFromObject(obj) {
      let el = document.createElement(obj.tag);
      for (const k in obj) {
        if (k === 'children')
          obj.children.forEach(c => el.appendChild(this.#createElementFromObject(c)));
        else if (k === 'text')
          el.textContent = obj[k];
        else if (k === 'ref')
          this.#ref[obj[k]] = el;
        else if (k !== 'tag')
          el.setAttribute(k, obj[k]);
      }
      return el;
    }

    constructor() {
      super();

      // 创建一个 shadow root
      let shadow = this.attachShadow({mode: 'open'});

      [
        {tag: 'link', rel: 'stylesheet', href: 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css'},
        {tag: 'link', rel: 'stylesheet', href: 'https://fonts.googleapis.com/icon?family=Material+Icons'},
        {tag: 'script', src: 'https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js'},
        {tag: 'button', class: 'mdc-fab mdc-fab--extended', style: 'margin: 10px', children: [
            {tag: 'div', class: 'mdc-fab__ripple'},
            {tag: 'span', class: 'material-icons mdc-fab__icon', ref: 'icon'},
            {tag: 'span', class: 'mdc-fab__label', ref: 'text'},
          ]},
        {tag: 'style', text: `
          :host {
            /* --mdc-theme-primary: #e74c3c; */
            --mdc-theme-secondary: #fafafa;
            /* --mdc-theme-background: #f00; */
            /* --mdc-theme-on-primary: #fff; */
            --mdc-theme-on-secondary: #2c3e50;
          }
        `},
      ].forEach(x => shadow.appendChild(this.#createElementFromObject(x)));

      this.onclick = this.recover;
    }

    get text() {
      return this.#ref.text.textContent;
    }

    set text(s) {
      this.#ref.text.textContent = s;
    }

    get icon() {
      return this.#ref.icon.textContent;
    }

    set icon(s) {
      this.#ref.icon.textContent = s;
    }

    // onclick() {  // 为什么不能这样重写方法??
    recover() {
      this.#media_node.src &&= this.#media_src;
      this.parentNode.replaceChild(this.#orig_node, this);
      delete this;
    }

    // 替换掉旧节点,展示本按钮
    replace(root_node, media_node) {
      this.#orig_node = root_node.parentNode.replaceChild(this, root_node);
      this.#media_node = media_node;
      this.#media_src = media_node.src;
      media_node.src &&= ' ';

      // 询问目标链接大小,太大则等待用户选择,较小直接显示
      if (this.#media_src) {
        fetch(this.#media_src, {method: 'HEAD'}).then(resp => {
          if (resp.ok) {
            const size = parseInt(resp.headers.get('content-length'));
            if (size < (1 << 20) && this.icon === blocks.userimg.icon)
              this.recover();
            else if (size > 0)
              this.text += `(${formatSize(size)})`;
          }
        });
      }
    }
  });

  document.addEventListener('DOMContentLoaded', () => {
    document.querySelectorAll(Object.keys(blocks).map(x => `.${x}`).join()).forEach(x => {

      // 获取 element 类型、显示按钮时的图标
      const {type, icon} = Object.entries(blocks).find(([k]) => x.classList.contains(k))[1];

      // 若图片已下载完毕,就让它显示吧
      if (type === 'img' && x.complete)
        return;

      // 获取真正的多媒体节点
      const media_node = ['audio', 'video'].includes(type) ? x.querySelector(type) : x;

      // 图片有外层链接,则准备替换整个外层
      if (type === 'img' && x.parentNode.href)
        x = x.parentNode;

      // 创建按钮
      let el = document.createElement('my-button');
      el.icon = icon;
      el.text = type === 'iframe' ? '内嵌网页' : media_node.alt || media_node.src;
      el.replace(x, media_node);
    });
  });
</script>
(/@Ta/2022-07-31 07:15//)

8. 这个有啥用处
(/@Ta/2022-07-30 19:32//)

9. 试试IMG_20201011_134055.jpg(2.89 MB)
Redmi K30 Pro
(/@Ta/2022-07-30 19:37//)

10.

@大尨,省流量

防止一打开页面,十几MB的图片、几十MB的音乐视频,哗哗地下,流量刷刷地跑,话费蹭蹭地掉

(/@Ta/2022-07-30 19:38//)

11. @无名啊,好吧 各有好处。
(/@Ta/2022-07-30 19:40//)

12. Screenshot_2022-07-30-19-38-27-651_com.quark.browser.jpg(458.02 KB)会和这个插件有冲突 https://hu60.cn/q.php/bbs.topic.103359.html @老虎会游泳
Redmi K30 Pro
(/@Ta/2022-07-30 19:41//)

13.

@大尨,小众插件

(/@Ta/2022-07-30 19:42//)

14.

@无名啊,冲突能解决吗
一加8Pro

(/@Ta/2022-07-30 22:26//)

15.

@无名啊,我猜viewer.js是通过addEventListener的方式添加点击事件的。

(/@Ta/2022-07-31 09:54//)

16.

@无名啊有没有直接获取文件大小的方法,有,file.hu60.cn的文件大小在URL末尾。此外新上传的图片,alt里也会有文件大小。

    function getSizeFromUrl(url) {
      url = hu60_decode_url64(url);
      var parts = url.match(/\/file\/(?:hash\/[^\/]+\/[a-f0-9]{32}|uuid\/[^\/]+\/[a-f0-9-]{36})(\d+)\b/);
      return parts ? parseInt(parts[1]) : 0;
    }
    function getImgSize(img) {
      if (img.alt) {
        var parts = img.alt.match(/([0-9]+(:?\.[0-9]+)?\s*(?:[KMGT]B|bytes))/);
      	if (parts) return parts[1];
      }
      var size = getSizeFromUrl(img.src);
      return size > 0 ? humanize.filesize(size) : '';
    }
(/@Ta/2022-07-31 10:52//)

17.

@老虎会游泳,我用的我7楼的代码,有小概率能用上图片浏览器(此时正常,点击按钮重现图片后也正常),大部分时候被你的 footer.js 代替了,点击图片就新窗口打开

(/@Ta/2022-07-31 12:54//)

18.

@老虎会游泳,旧图片、外站图片、音乐、视频啥的,好像没有提供文件大小

对这些情况,有啥更好的办法吗(除了HEAD再查询一次)

(/@Ta/2022-07-31 12:59//)

19.

@无名啊所有位于file.hu60.cn的文件都可以使用

function getSizeFromUrl(url) {
      url = hu60_decode_url64(url);
      var parts = url.match(/\/file\/(?:hash\/[^\/]+\/[a-f0-9]{32}|uuid\/[^\/]+\/[a-f0-9-]{36})(\d+)\b/);
      return parts ? parseInt(parts[1]) : 0;
    }

获取文件大小。

文件路径是这样生成的:

        if ($fileMd5 !== null) {
            $key = 'file/hash/' . $type . '/' . $fileMd5 . $fileSize . $ext;
        } else {
            $uuid = str::guidv4();
            $key = 'file/uuid/' . $type . '/' . $uuid . $fileSize . $ext;
        }
(/@Ta/2022-07-31 13:05//)

下一页 1/4页,共72楼

回复需要登录

11月27日 07:55 星期三

本站由hu60wap6华为CPU驱动

备案号: 京ICP备18041936号-1