// ==UserScript== // @name 动漫之家漫画页转长图 // @namespace https://unlucky.ninja/ // @version 2024.06.18.2 // @description 打开某一话后点击标题右侧按钮生成长图(仅为用于熟人间分享),需要与网络请求修改类浏览器扩展配合 // @author UnluckyNinja // @license MIT // @match https://manhua.idmzj.com/* // @match https://manhua.dmzj.com/* // @require https://code.jquery.com/jquery-3.7.1.slim.min.js // @require https://update.greasyfork.icu/scripts/498113/1395364/waitForKeyElements_mirror.js // @icon https://www.google.com/s2/favicons?sz=64&domain=idmzj.com // @grant none // @downloadURL https://update.greasyfork.icu/scripts/498114/%E5%8A%A8%E6%BC%AB%E4%B9%8B%E5%AE%B6%E6%BC%AB%E7%94%BB%E9%A1%B5%E8%BD%AC%E9%95%BF%E5%9B%BE.user.js // @updateURL https://update.greasyfork.icu/scripts/498114/%E5%8A%A8%E6%BC%AB%E4%B9%8B%E5%AE%B6%E6%BC%AB%E7%94%BB%E9%A1%B5%E8%BD%AC%E9%95%BF%E5%9B%BE.meta.js // ==/UserScript== (function() { 'use strict'; // consts const CANVAS_WIDTH = 800 const JPG_QUALITY = 0.5 // return tarB in tarA/tarB=srcA/srcB function toRatioValue(srcA, srcB, tarA){ return tarA/srcA * srcB } function getTotalHeight(images, width = 800){ let totalH = 0 for (let image of images){ const w = image.naturalWidth const h = image.naturalHeight totalH += Math.ceil(toRatioValue(w, h, width)) } return totalH } async function genLongImageBlob(){ const images = [...document.querySelectorAll(".comic_wraCon img").values()] const canvas = document.createElement('canvas') const totalHeight = getTotalHeight(images, CANVAS_WIDTH) let targetWidth = CANVAS_WIDTH if (totalHeight > 32767) { let optimalWidth = Math.floor(32767 / totalHeight * CANVAS_WIDTH) const conti = confirm(`高度超出生成上限: ${totalHeight} / 32767, 若继续生成,则图片宽度将只有:${optimalWidth.toFixed(0)}px,是否继续生成?`) if (!conti) throw new Error('高度过大无法绘制') targetWidth = optimalWidth } canvas.width = targetWidth canvas.height = getTotalHeight(images, targetWidth) const context = canvas.getContext('2d') let curH = 0 const promises = images.map(it=>it.decode()) await Promise.all(promises) for (let image of images){ const w = image.naturalWidth const h = image.naturalHeight const newH = Math.ceil(toRatioValue(w, h, targetWidth)) console.log(canvas.width) console.log(canvas.height) context.drawImage(image, 0, curH, targetWidth, newH) curH += newH } return new Promise((resolve)=>{ canvas.toBlob((blob)=>{ resolve(blob) }, 'image/jpeg', JPG_QUALITY) }) } let downloadButton let previewButton async function openImage(){ previewButton.innerText = '生成中...' previewButton.disabled = true try { const blob = await genLongImageBlob() const url = URL.createObjectURL(blob) window.open(url, '_blank') setTimeout(()=>{ URL.revokeObjectURL(url) }, 10000) } finally { previewButton.innerText = '预览长图' previewButton.disabled = false } } async function downloadImage(){ downloadButton.innerText = '生成中...' downloadButton.disabled = true try { const blob = await genLongImageBlob() const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url const name = document.querySelector('.comic_wraCon h1').textContent const chapter = document.querySelector('.comic_wraCon h1 ~ span').textContent a.download = `${name}_${chapter}.jpg` a.click() setTimeout(()=>{ URL.revokeObjectURL(url) }, 0) } finally { downloadButton.innerText = '下载长图' downloadButton.disabled = false } } function addButton(node){ const wrapper = document.createElement('span') wrapper.style.position = 'relative' const div = document.createElement('div') div.style.display = 'flex' div.style.position = 'absolute' div.style.width = 'max-content' div.style.height = '100%' div.style.left = '100%' div.style.top = '0' previewButton = document.createElement('button') previewButton.innerText = '预览长图' previewButton.style.margin = '0 0.5rem' previewButton.addEventListener('click', openImage) div.append(previewButton) downloadButton = document.createElement('button') downloadButton.innerText = '下载长图' //downloadButton.style.margin = '0 0.5rem' downloadButton.addEventListener('click', downloadImage) div.append(downloadButton) wrapper.append(div) node[0].parentElement.append(wrapper) } waitForKeyElements('.comic_wraCon img', (images)=>{ if (!window.location.href.match(/https?:\/\/manhua.i?dmzj.com\/.*\/.*.shtml.*/)) return images[0].crossOrigin = 'anonymous' }) waitForKeyElements('.comic_wraCon h1', (nodes)=>{ if (!window.location.href.match(/https?:\/\/manhua.i?dmzj.com\/.*\/.*.shtml.*/)) return addButton(nodes) }) })();