Files
PC-Light-Forum/component/preview-image/preview.js
DESKTOP-RQ919RC\Pc f73a662141 feat: 新增签到功能模块并优化详情页样式
- 添加签到功能相关HTML/CSS/JS文件
- 在详情页增加图片预览组件
- 调整详情页文本行高和标题样式
- 修复评论时间显示问题
- 优化本地开发环境授权处理
2025-11-21 19:07:03 +08:00

100 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;