100 lines
5.4 KiB
JavaScript
100 lines
5.4 KiB
JavaScript
class PreviewImage extends HTMLElement {
|
||
static get observedAttributes() { return ["src", "alt"]; }
|
||
constructor(){
|
||
super();
|
||
this._src = this.getAttribute("src") || "";
|
||
this._alt = this.getAttribute("alt") || "";
|
||
this.attachShadow({ mode: "open" });
|
||
const root = document.createElement("div");
|
||
root.className = "root";
|
||
const img = document.createElement("img");
|
||
root.appendChild(img);
|
||
this.shadowRoot.append(style, root);
|
||
this._img = img;
|
||
this._render();
|
||
this._img.addEventListener("click", (e)=>{ e.stopPropagation(); this.init(this._src); });
|
||
// 保存一个全局实例(与 bi.js 风格一致,便于外部静态调用)
|
||
window.previewImageComponent = this;
|
||
}
|
||
attributeChangedCallback(name, oldVal, newVal){
|
||
if(name === "src") this._src = newVal || "";
|
||
if(name === "alt") this._alt = newVal || "";
|
||
this._render();
|
||
}
|
||
_render(){
|
||
if(this._img){ this._img.src = this._src; this._img.alt = this._alt; }
|
||
}
|
||
init(src){ PreviewImage.open(src || this._src); }
|
||
static open(src){
|
||
if(!src) return;
|
||
const mask = document.createElement("div");
|
||
const box = document.createElement("div");
|
||
const img = document.createElement("img");
|
||
|
||
mask.style.width = "100%";
|
||
mask.style.height = "100%";
|
||
mask.style.maxWidth = "none";
|
||
mask.style.maxHeight = "none";
|
||
mask.style.border = "none";
|
||
mask.style.position = "fixed";
|
||
mask.style.top = "0";
|
||
mask.style.left = "0";
|
||
mask.style.backgroundColor = "rgba(255, 255, 255, 0.8)";
|
||
mask.style.zIndex = "10002";
|
||
mask.style.display = "flex";
|
||
mask.style.alignItems = "center";
|
||
mask.style.justifyContent = "center";
|
||
|
||
// box.className = "detail-image flexcenter";
|
||
box.style.width = "80vw";
|
||
box.style.height = "80vh";
|
||
box.style.borderRadius = "8px";
|
||
box.style.backgroundColor = "rgba(17, 17, 17, 0.9)";
|
||
box.style.overflow = "hidden";
|
||
box.style.display = "flex";
|
||
box.style.alignItems = "center";
|
||
box.style.justifyContent = "center";
|
||
box.style.position = "relative";
|
||
|
||
// img.className = "detail-img";
|
||
img.style.maxWidth = "100%";
|
||
img.style.maxHeight = "100%";
|
||
img.src = src;
|
||
box.appendChild(img);
|
||
mask.appendChild(box);
|
||
let scale = 1, tx = 0, ty = 0, dragging = false, sx = 0, sy = 0;
|
||
const apply = () => { img.style.transform = `translate(${tx}px, ${ty}px) scale(${scale})`; img.style.cursor = dragging ? "grabbing" : "grab"; };
|
||
const onWheel = (ev) => { ev.preventDefault(); const step = 0.1; scale += (ev.deltaY > 0 ? -step : step); if (scale < 0.1) scale = 0.1; if (scale > 5) scale = 5; apply(); };
|
||
const onDown = (ev) => { ev.preventDefault(); dragging = true; sx = ev.clientX; sy = ev.clientY; apply(); };
|
||
const onMove = (ev) => { if (!dragging) return; const dx = ev.clientX - sx, dy = ev.clientY - sy; sx = ev.clientX; sy = ev.clientY; tx += dx; ty += dy; apply(); };
|
||
const onUp = () => { dragging = false; apply(); };
|
||
img.addEventListener("wheel", onWheel, { passive: false }); img.addEventListener("mousedown", onDown); document.addEventListener("mousemove", onMove); document.addEventListener("mouseup", onUp); img.addEventListener("click", (ev) => ev.stopPropagation());
|
||
const onKey = (ev) => { if (ev.key === "Escape") close(); };
|
||
const close = () => {
|
||
document.body.style.overflow = "auto";
|
||
img.removeEventListener("wheel", onWheel);
|
||
img.removeEventListener("mousedown", onDown);
|
||
document.removeEventListener("mousemove", onMove);
|
||
document.removeEventListener("mouseup", onUp);
|
||
document.removeEventListener("keydown", onKey);
|
||
mask.remove();
|
||
};
|
||
mask.addEventListener("click", (ev)=>{ if (ev.target === mask) close(); });
|
||
box.addEventListener("click", (ev)=>ev.stopPropagation());
|
||
const dl = document.createElement("button"); dl.textContent = "下载图片"; Object.assign(dl.style, { position: "absolute", right: "24px", bottom: "24px", zIndex: "10001", padding: "8px 14px", borderRadius: "6px", border: "none", cursor: "pointer", backgroundColor: "#50e3c2", color: "#000" });
|
||
dl.addEventListener("click", (ev) => { ev.stopPropagation(); const name = (src.split("/").pop() || "image").split("?")[0]; fetch(src).then((r) => r.blob()).then((blob) => { const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = name || "image"; document.body.appendChild(a); a.click(); a.remove(); setTimeout(()=>URL.revokeObjectURL(url),0); }).catch(() => { const a = document.createElement("a"); a.href = src; a.target = "_blank"; document.body.appendChild(a); a.click(); a.remove(); }); });
|
||
box.appendChild(dl);
|
||
document.body.appendChild(mask);
|
||
document.addEventListener("keydown", onKey);
|
||
document.body.style.overflow = "hidden";
|
||
apply();
|
||
}
|
||
}
|
||
if (!customElements.get("preview-image")) customElements.define("preview-image", PreviewImage);
|
||
// 3. 暴露统一对象(可使用 previewImage.initComponent(url))
|
||
PreviewImage.initComponent = function(url){
|
||
if (window.previewImageComponent) { window.previewImageComponent.init(url); return true; }
|
||
PreviewImage.open(url); return true;
|
||
};
|
||
window.previewImage = PreviewImage;
|