viewerjs-optimize
Version:
JavaScript image viewer-optimize.
283 lines (240 loc) • 7.14 kB
JavaScript
import {
CLASS_LOADING,
CLASS_TRANSITION,
EVENT_LOAD,
EVENT_TRANSITION_END,
EVENT_VIEWED,
} from './constants';
import {
addClass,
addListener,
assign,
escapeHTMLEntities,
forEach,
getImageNameFromURL,
getImageNaturalSizes,
getTransforms,
isFunction,
isString,
removeClass,
removeListener,
setData,
setStyle,
} from './utilities';
export default {
render() {
this.initContainer();
this.initViewer();
this.initList();
this.renderViewer();
},
initContainer() {
this.containerData = {
width: window.innerWidth,
height: window.innerHeight,
};
},
initViewer() {
const { options, parent } = this;
let viewerData;
if (options.inline) {
viewerData = {
width: Math.max(parent.offsetWidth, options.minWidth),
height: Math.max(parent.offsetHeight, options.minHeight),
};
this.parentData = viewerData;
}
const containerBox = document.querySelector('#viewer-canvas');
viewerData = {
width: Math.max(containerBox.offsetWidth, options.minWidth),
height: Math.max(containerBox.offsetHeight, options.minHeight),
};
// eslint-disable-next-line prefer-template
const elementClassName = '.' + options.className.split(' ')[1];
const div1 = document.querySelector(elementClassName);
div1.onmousedown = function mouseDownFunction(ev) {
const oevent = ev || window.event;
const distanceX = oevent.clientX - div1.offsetLeft;
const distanceY = oevent.clientY - div1.offsetTop;
document.onmousemove = function mouseMoveFunction(evs) {
const oevents = evs || window.event;
div1.style.left = `${oevents.clientX - distanceX}px`;
div1.style.top = `${oevents.clientY - distanceY}px`;
};
document.onmouseup = function mouseUpFunction() {
document.onmousemove = null;
document.onmouseup = null;
};
};
// if (this.fulled || !viewerData) {
// viewerData = this.containerData;
// }
this.viewerData = assign({}, viewerData);
},
renderViewer() {
if (this.options.inline && !this.fulled) {
setStyle(this.viewer, this.viewerData);
}
},
initList() {
const { element, options, list } = this;
const items = [];
forEach(this.images, (image, i) => {
const src = escapeHTMLEntities(image.src);
const alt = escapeHTMLEntities(image.alt || getImageNameFromURL(src));
let { url } = options;
if (isString(url)) {
url = escapeHTMLEntities(image.getAttribute(url));
} else if (isFunction(url)) {
url = escapeHTMLEntities(url.call(this, image));
}
if (src || url) {
items.push('<li>'
+ '<img'
+ ` src="${src || url}"`
+ ' role="button"'
+ ' data-viewer-action="view"'
+ ` data-index="${i}"`
+ ` data-original-url="${url || src}"`
+ ` alt="${alt}"`
+ '>'
+ '</li>');
}
});
list.innerHTML = items.join('');
this.items = list.getElementsByTagName('li');
forEach(this.items, (item) => {
const image = item.firstElementChild;
setData(image, 'filled', true);
if (options.loading) {
addClass(item, CLASS_LOADING);
}
addListener(image, EVENT_LOAD, (event) => {
if (options.loading) {
removeClass(item, CLASS_LOADING);
}
this.loadImage(event);
}, {
once: true,
});
});
if (options.transition) {
addListener(element, EVENT_VIEWED, () => {
addClass(list, CLASS_TRANSITION);
}, {
once: true,
});
}
},
renderList(index) {
const i = index || this.index;
const width = this.items[i].offsetWidth || 30;
const outerWidth = width + 1; // 1 pixel of `margin-left` width
// Place the active item in the center of the screen
setStyle(this.list, assign({
width: outerWidth * this.length,
}, getTransforms({
translateX: 0,
// translateX: ((this.viewerData.width - width) / 2) - (outerWidth * i),
})));
},
resetList() {
const { list } = this;
list.innerHTML = '';
removeClass(list, CLASS_TRANSITION);
setStyle(list, getTransforms({
translateX: 0,
}));
},
initImage(done) {
const { options, image, viewerData } = this;
const footerHeight = this.footer.offsetHeight;
const viewerWidth = viewerData.width;
const viewerHeight = Math.max(viewerData.height - footerHeight, footerHeight);
const oldImageData = this.imageData || {};
let sizingImage;
this.imageInitializing = {
abort() {
sizingImage.onload = null;
},
};
sizingImage = getImageNaturalSizes(image, (naturalWidth, naturalHeight) => {
const aspectRatio = naturalWidth / naturalHeight;
let width = viewerWidth;
let height = viewerHeight;
this.imageInitializing = false;
if (viewerHeight * aspectRatio > viewerWidth) {
height = viewerWidth / aspectRatio;
} else {
width = viewerHeight * aspectRatio;
}
width = Math.min(width * 0.9, naturalWidth);
height = Math.min(height * 0.9, naturalHeight);
const imageData = {
naturalWidth,
naturalHeight,
aspectRatio,
ratio: width / naturalWidth,
width,
height,
left: (viewerWidth - width) / 2,
top: (viewerHeight - height) / 2,
};
const initialImageData = assign({}, imageData);
if (options.rotatable) {
imageData.rotate = oldImageData.rotate || 0;
initialImageData.rotate = 0;
}
if (options.scalable) {
imageData.scaleX = oldImageData.scaleX || 1;
imageData.scaleY = oldImageData.scaleY || 1;
initialImageData.scaleX = 1;
initialImageData.scaleY = 1;
}
this.imageData = imageData;
this.initialImageData = initialImageData;
if (done) {
done();
}
});
},
renderImage(done) {
const { image, imageData } = this;
setStyle(image, assign({
width: imageData.width,
height: imageData.height,
// XXX: Not to use translateX/Y to avoid image shaking when zooming
marginLeft: imageData.left,
marginTop: imageData.top,
}, getTransforms(imageData)));
if (done) {
if ((this.viewing || this.zooming) && this.options.transition) {
const onTransitionEnd = () => {
this.imageRendering = false;
done();
};
this.imageRendering = {
abort() {
removeListener(image, EVENT_TRANSITION_END, onTransitionEnd);
},
};
addListener(image, EVENT_TRANSITION_END, onTransitionEnd, {
once: true,
});
} else {
done();
}
}
},
resetImage() {
// this.image only defined after viewed
if (this.viewing || this.viewed) {
const { image } = this;
if (this.viewing) {
this.viewing.abort();
}
image.parentNode.removeChild(image);
this.image = null;
}
},
};