vvcomponent
Version:
VV组件
114 lines (111 loc) • 5.95 kB
JavaScript
globalThis.ComponenetError = class extends Error {
constructor(message) {
super(message);
this.name = 'ComponenetError';
}
}
class aImage extends HTMLElement {
errorSvg = (w, h) => `<?xml version="1.0" encoding="UTF-8"?><svg width="${w}" height="${h}" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M44 23.9941C44 22.8896 43.1046 21.9941 42 21.9941C40.8954 21.9941 40 22.8896 40 23.9941H44ZM24 7.99414C25.1046 7.99414 26 7.09871 26 5.99414C26 4.88957 25.1046 3.99414 24 3.99414V7.99414ZM39 39.9941H9V43.9941H39V39.9941ZM8 38.9941V8.99414H4V38.9941H8ZM40 23.9941V38.9941H44V23.9941H40ZM9 7.99414H24V3.99414H9V7.99414ZM9 39.9941C8.44772 39.9941 8 39.5464 8 38.9941H4C4 41.7556 6.23857 43.9941 9 43.9941V39.9941ZM39 43.9941C41.7614 43.9941 44 41.7556 44 38.9941H40C40 39.5464 39.5523 39.9941 39 39.9941V43.9941ZM8 8.99414C8 8.44186 8.44771 7.99414 9 7.99414V3.99414C6.23858 3.99414 4 6.23272 4 8.99414H8Z" fill="#333"/><path d="M6 35L16.6931 25.198C17.4389 24.5143 18.5779 24.4953 19.3461 25.1538L32 36" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M28 31L32.7735 26.2265C33.4772 25.5228 34.5914 25.4436 35.3877 26.0408L42 31" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M33 7L41 15" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/><path d="M41 7L33 15" stroke="#333" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
cacheBlob = null;
imgUrl = null;
constructor() {
super();
this.attachShadow({ mode: 'open' });
const config = { attributes: true, childList: true, subtree: true, characterData: true };
const self = this;
const callback = function (mutationsList, observer) {
self.loadImg();
};
const observer = new MutationObserver(callback);
observer.observe(this, config);
}
async render(w, h, e) {
const imgSrc = this.getAttribute('src');
this.imgUrl = imgSrc;
let imgDataUrl = null;
if (this.cacheBlob) {
imgDataUrl = await this.BlobToDataUrl(this.cacheBlob);
}
const imgAlt = this.getAttribute('alt') || 'Image';
const imgWidth = this.getAttribute('width');
const imgHeight = this.getAttribute('height');
const scale = typeof this.getAttribute('scale') == 'string' ? Number(this.getAttribute('scale')) : this.getAttribute('scale') || 1;
const borderRadius = this.getAttribute('border-radius') || 0;
const border = this.getAttribute('border') || 0;
const borderColor = this.getAttribute('border-color') || '#000';
const borderStyle = this.getAttribute('border-style') || 'solid';
const mode = this.getAttribute('mode') || 'none';
const img = document.createElement('div');
img.setAttribute("alt", imgAlt);
img.setAttribute("class", "iftc-image");
img.style.backgroundImage = `url("${imgDataUrl || imgSrc}")`;
img.style.backgroundRepeat = 'no-repeat';
img.style.width = `${(imgWidth || w) * scale}px`;
img.style.height = `${(imgHeight || h) * scale}px`;
img.style.backgroundSize = `${(imgWidth || w) * scale}px ${(imgHeight || h) * scale}px`;
img.style.backgroundPosition = 'center center';
img.style.borderRadius = `${borderRadius}px`;
img.style.border = `${border}px ${borderStyle} ${borderColor}`;
this.shadowRoot.innerHTML = '';
if (e) {
img.innerHTML = `<div class="iftc-image-error">${e((imgWidth * scale) || 24, (imgHeight * scale) || 24)}</div>`
img.style.backgroundImage = 'url("")';
}
if (mode == 'cover') {
img.style.backgroundSize = 'cover';
} else if (mode == 'contain') {
img.style.backgroundSize = 'contain';
} else if (mode == 'stretch') {
img.style.backgroundSize = '100% 100%';
} else if (mode == 'none') { } else {
throw new ComponenetError('无效的mode');
}
this.shadowRoot.appendChild(img);
}
loadImg() {
const imgSrc = this.getAttribute('src');
if (this.cacheBlob && this.imgUrl == imgSrc) {
this.render(this.imgUrl.width, this.imgUrl.height);
return;
}
const img = new Image();
img.src = imgSrc;
img.onload = async () => {
this.cacheBlob = await this.getImgBlob();
this.render(img.width, img.height)
}
img.onerror = () => this.render(img.width, img.height, this.errorSvg)
}
set(name, value) {
this.setAttribute(name, value);
}
get(name) {
return this.getAttribute(name);
}
BlobToDataUrl(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onloadend = () => resolve(reader.result);
reader.onerror = () => reject(new Error('Failed to read blob'));
reader.readAsDataURL(blob);
});
}
getImgBlob() {
return new Promise((resolve, reject) => {
const img = new Image();
img.src = this.getAttribute('src');
img.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
if (blob) resolve(blob);
else reject(new Error('Failed to convert image to blob'));
}, 'image/png');
}
})
}
}
customElements.define('iftc-image', aImage);