imager - pod4g/tool GitHub Wiki
- 图片broken美化 ok
- 图片broken reload
- 图片loading
- 图片懒加载 ok
- 图片全屏查看 ok
- [新增] 图片裁切
这些基础功能需要应用在我们的产品线上,作为前端的基础服务。
目前完成了 图片全屏查看、图片懒加载
例子:http://t.diaox2.com/view/test/lyf/profile/
点击任意一张图片将会触发图片全屏查看
需要在手机端运行
/*
*
* Author: 李彦峰
* Licence: MIT
* Date: 2016-07-30
*
*/
(function(global, factory) {
'use strict';
// UMD setting
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = global.document ?
factory(global, true) :
function(w) {
if (!w.document) {
throw new Error("Imager requires a window with a document");
}
return factory(w);
};
} else {
factory(global);
}
})(typeof window !== 'undefined' ? window : this, function(window, noGlobal) {
'use strict';
var document = window.document;
var html = document.documentElement;
var body = document.body;
var viewportHeight = html.clientHeight;
// var html = document.documentElement;
// 工具方法
// Object.assign是浅拷贝
if (typeof Object.assign !== 'function') {
Object.assign = function(target) {
if (target == null) {
throw new TypeError('Cannot convert undefined or null to object');
}
target = Object(target); // 转成对象
// 处理除了target之外的参数
for (var index = 1, source; source = arguments[index++];) { //eslint-disable-line
if (source != null) {
var keys = Object.keys(source);
// 使用keys + for 替换掉 forin + hasOwnProperty 加快遍历速度
// https://github.com/pod4g/tool/wiki/各种遍历的性能
for (var i = 0, key; key = keys[i++];) { //eslint-disable-line
target[key] = source[key];
}
}
}
return target;
};
}
// 扩展HTMLElement原型
Object.assign(HTMLElement.prototype, {
addClass: function(klass) {
this.classList.add(klass);
return this;
},
removeClass: function(klass) {
this.classList.remove(klass);
return this;
},
toggleClass: function(klass) {
this.classList.toggle(klass);
return this;
},
hasClass: function(klass) {
return this.classList.contains(klass);
},
html: function(content) {
this.innerHTML = content;
return this;
},
text: function(content) {
this.textContent = content;
return this;
}
});
function getFirstOrAll(arr) {
if (arr && arr.length === 1) {
return arr[0];
} else {
return arr;
}
}
/**
* @param {[String]} id
* @return {[object Element]}
*/
function getById(id) {
return document.getElementById(id);
}
/**
* @param {[String]} calssName
* @param {[object Node]} parent
* @return {[object HTMLCollection]} [类数组]
*/
function getByClass(calssName, p) {
return getFirstOrAll((p || document).getElementsByClassName(calssName));
}
/**
* @param {[String]} calssName
* @param {[object Node]} parent
* @return {[object HTMLCollection]} [类数组]
*/
function getByTag(calssName, p) {
return getFirstOrAll((p || document).getElementsByTagName(calssName));
}
/**
* @param {[String]} tagName
* @param {[object Node]} parent
* @return {[object HTMLCollection]} [类数组]
*/
function parent(ele) {
return ele.parentElement;
}
// lazy 模块
// error image 美化
// error image reload
// image view
// 图片懒加载模块
// lazy.js
// 图片breaken美化
// breaken.js
// 图片breaken reload
// reload.js
// 图片全屏查看
// view.js
// 图片滑动
// swipe.js
// util.js
// 支持UMD
function addEvent(element, type, fn, capture) {
element.addEventListener(type, fn, isNullOrUndefined(capture) ? false : capture); //eslint-disable-line
}
// function removeEvent(element, type, fn){
// element.removeEventListener(type, fn);
// }
function queryFrom(pElement) {
pElement = pElement || document;
return function(selector) {
return pElement.querySelectorAll(selector);
};
}
function isElement(element) {
return element && element.nodeType;
}
function create(tag) {
return document.createElement(tag);
}
function isNullOrUndefined(un) {
// return un == void 0;
return un == null;
}
function clone(element, deep) {
return element.cloneNode(isNullOrUndefined(deep) ? false : deep); //eslint-disable-line
}
function cssPrefix(key, value) {
var list = ['-webkit-', '-moz-', '-ms-', '-o-', ' '];
var ret = '';
value = value || ret;
for (var i = 0, prefix; prefix = list[i++];) { //eslint-disable-line
ret += prefix + key + ':' + value + ';';
}
return ret;
}
function removeAttribute(element, attr) {
element.removeAttribute(attr);
}
function takeOff(element) {
var toRemovedAttrs = ['class', 'style', 'width', 'height', 'id', 'data-swipe'];
for (var i = 0, attr; attr = toRemovedAttrs[i++];) { //eslint-disable-line
removeAttribute(element, attr);
}
return element;
}
function getStyle(element, style) {
return window.getComputedStyle(element, null)[style];
}
function doNothing(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
}
var Swipe = function() {
this.prefix = 'swipe-overlay';
this.show = this.prefix + '-show';
this.flag = 'data-swipe';
this.event = 'click';
this.index = 0;
this.iw = window.innerWidth;
this.ih = window.innerHeight;
// 屏幕宽高比
this.radio = this.iw / this.ih;
// 创建基础容器
this.createView();
// 初始化
this.init();
// 绑定事件
this.bindEvent();
};
Object.assign(Swipe.prototype, {
createView: function() {
// 最外层容器
var wrapper = getById(this.prefix) || getByClass(this.prefix);
var ul;
if (!wrapper) {
// 列表容器
wrapper = create('div');
wrapper.addClass(this.prefix);
wrapper.id = this.prefix;
wrapper.html('<p class="' + this.prefix + '-index-and-length">' +
'<span class="' + this.prefix + '-index"></span> / <span class="' + this.prefix + '-length"></span>' +
'</p>');
ul = create('ul');
wrapper.appendChild(ul);
body.appendChild(wrapper);
}
this.wrapper = wrapper;
this.ul = ul || getByClass(this.prefix + '-list', this.wrapper);
},
init: function() {
var $ = queryFrom(body);
var imgs = $('[' + this.flag + ']');
this.swipeImgs = [];
this.length = imgs.length;
var ul = this.ul;
var self = this;
addEvent(body, self.event, function(e) {
// 不阻止,否则会影响点击锚点
// doNothing(e);
var target = e.target;
if (target.hasAttribute(self.flag)) {
self.wrapper.addClass(self.show);
for (i = 0; img = imgs[i++];) { //eslint-disable-line
if (target === img) {
self.index = i - 1;
break;
}
}
getByClass(self.prefix + '-index').text(self.index + 1);
getByClass(self.prefix + '-length').text(self.length);
if (self.ul.children.length > 0) {
for (var i = 0, img; img = self.swipeImgs[i++];) { //eslint-disable-line
parent(img).style.cssText = cssPrefix('transform', 'translate3d(' + self.iw * (i - 1 - self.index) + 'px,0,0)');
}
} else {
var frag = document.createDocumentFragment();
for (i = 0; img = imgs[i++];) { //eslint-disable-line
var li = create('li');
var dataSrc = img.dataset.src;
var cloneImg;
if (dataSrc && dataSrc !== img.src) {
cloneImg = new Image();
cloneImg.src = dataSrc;
cloneImg.alt = img.alt || '';
cloneImg.onload = function() {
self.adjustImg(this);
} //eslint-disable-line
} else {
cloneImg = takeOff(clone(img));
}
li.style.cssText = cssPrefix('transform', 'translate3d(' + self.iw * (i - 1 - self.index) + 'px,0,0)');
li.appendChild(cloneImg);
frag.appendChild(li);
self.swipeImgs.push(cloneImg);
}
ul.appendChild(frag);
addEvent(self.wrapper, self.event, function(ev) {
doNothing(ev);
this.removeClass(self.show);
});
}
self.renderDOM();
}
});
},
adjustImg: function(img) {
if ( isElement(img) ) {
var width = parseInt(getStyle(img, 'width'));
var height = parseInt(getStyle(img, 'height'));
// 如果是一张小图,原样显示
if (width < this.iw && height < this.ih) {
return;
}
var scale = width / height;
// 说明这个图片是一个宽图
if (scale > this.radio) {
img.width = this.iw;
} else if (scale < this.radio) { // 长图
img.height = this.ih;
}
}
},
renderDOM: function() {
var swipeImgs = this.swipeImgs;
// 处理要滑动的图片
for (var i = 0, img; img = swipeImgs[i++];) { //eslint-disable-line
this.adjustImg(img);
}
},
bindEvent: function() {
var self = this;
var startHandler = function(e) {
self.startX = e.touches[0].pageX;
self.offsetX = 0;
self.startTime = +new Date();
};
var moveHandler = function(e) {
self.offsetX = e.touches[0].pageX - self.startX;
var i = self.index - 1;
var m = self.index + 2;
var lis = this.children;
for (; i < m; i++) {
var li = lis[i];
if (li) {
li.style.cssText =
cssPrefix('transform', 'translate3d(' + ((i - self.index) * self.iw + self.offsetX) + 'px,0,0)');
}
}
};
var endHandler = function() {
if (self.offsetX === 0) {
return;
}
// 屏幕的 1/5 为边界
var boundary = self.iw / 5;
var endTime = +new Date();
// offsetX 左负,右正,左下(一页),右上(一页)
// 慢操作
if (endTime - self.startTime > 700) {
// 向右滑动
if (self.offsetX >= boundary) { // case 1
// 进入上一页
self.go(-1);
// }else if(self.offsetX < boundary){ // case 2 bug
// 向左滑动
} else if (self.offsetX < -boundary) { // case 2 bug already fixed
// bug描述
// 如果是向右滑动一段很小的距离,offsetX是一个很小的正数,
// 此时offsetX显然是小于boundary的
// 此时期望走 case 3 ,即留在本页,但却走了case2,即进入下一页
// 本来case2 是向左滑动的case,但是boundary前忘写了符号,所以就出现了这个
// 困扰我2天的bug
// 这个bug只有在慢操作下才会触发
// 因为快操作向左滑动的case 已经有符号
// 进入下一页
self.go(1);
} else { // case 3
// 偏移太小,留在本页
self.go();
}
} else { // 快操作
if (self.offsetX >= 50) {
// 进入上一页
self.go(-1);
} else if (self.offsetX < -50) {
// 进入下一页
self.go(1);
} else {
// 偏移太小,留在本页
self.go();
}
}
};
addEvent(this.ul, 'touchstart', startHandler);
addEvent(this.ul, 'touchmove', moveHandler);
addEvent(this.ul, 'touchend', endHandler);
},
go: function(dir) {
dir = dir || 0;
var lis = this.ul.children;
var nindex = this.index + dir;
var length = this.length;
// 索引超出处理
if (nindex > length - 1) {
nindex = length - 1;
} else if (nindex < 0) {
nindex = 0;
}
getByClass(this.prefix + '-index', this.wrapper).text(nindex + 1);
var transition = cssPrefix('transition', 'all .2s ease-out');
lis[this.index].style.cssText = cssPrefix('transform', 'translate3d(0,0,0)') + transition;
lis[nindex].style.cssText = cssPrefix('transform', 'translate3d(0,0,0)') + transition;
var p = nindex - 1;
var n = nindex + 1;
if (p >= 0) {
lis[p].style.cssText = cssPrefix('transform', 'translate3d(' + (-this.iw) + 'px,0,0)') + transition;
}
if (n <= length - 1) {
lis[n].style.cssText = cssPrefix('transform', 'translate3d(' + (this.iw) + 'px,0,0)') + transition;
}
this.index = nindex;
}
});
// lazy-load模块
// 判断一个元素是否在可视区(视口)内部
function inViewport(element) {
var top, scrollTop, total, offsetTop;
if (isElement(element)) {
top = element.getBoundingClientRect().top;
if (top <= 0) {
return false;
}
scrollTop = body.scrollTop || html.scrollTop;
total = scrollTop + viewportHeight;
offsetTop = scrollTop + top;
if (offsetTop < total) {
return true;
}
}
return false;
}
function isLazyImg(img) {
var dataSrc = img.dataset.src;
if ( isElement(img) && img.tagName === 'IMG' && dataSrc && dataSrc !== img.src ) {
return true;
}
return false;
}
function getShouldLazyLoadImgs() {
var shouldLazyLoad = [];
var lazyFlagElements = document.querySelectorAll('img[data-lazy-src]');
// debugger;
// 取出所有需要lazyload的图片
// 去重
// 去掉没有data-src的img
for (var i = 0, ele; ele = lazyFlagElements[i++];) { //eslint-disable-line
// if ( ele.tagName !== 'IMG' ) {
// // shouldLazyLoad.push.apply(shouldLazyLoad, );
// var imgs = ele.getElementsByTagName('img');
// for (var j = 0, img; img = imgs[j++];) {
// if (isLazyImg(img) && shouldLazyLoad.indexOf(img) === -1 ) {
// shouldLazyLoad.push(img);
// }
// }
// } else if (isLazyImg(ele) && shouldLazyLoad.indexOf(ele) === -1) {
shouldLazyLoad.push(ele);
// }
}
return shouldLazyLoad;
}
var scrollHandler = function() {
var shouldLazyLoad = getShouldLazyLoadImgs();
console.log(shouldLazyLoad);
return function() {
for (var i = 0, img; img = shouldLazyLoad[i++];) { //eslint-disable-line
if (inViewport(img)) {
img.src = img.dataset.lazySrc;
img.onerror = function(){ this.src = 'http://t.diaox2.com/view/test/lyf/promotion/images/cd.png';}
}
}
}
}();
window.addEventListener('scroll', scrollHandler);
scrollHandler();
// addEvent(window, 'load', function(){
// var imgs = getByTag('img'), brokens = [], i = 0, img;
// while( img = imgs[i++] ){
// if( !img.complete || ( img.naturalWidth === 0 && img.naturalHeight === 0 ) ){
// brokens.push(img);
// var span = create('span');
// span.text('加载错误');
// }
// }
// console.log(brokens);
// });
function imager() {
var swipe = new Swipe();
imager = function() {
return swipe;
}
return swipe;
}
if (!noGlobal) {
window.imager = imager;
}
if (body.hasAttribute('data-img-swipe-on')) {
imager();
// getByTag('img')[0].click();
}
// alert(getComputedStyle(getById('node'),':before').content);
// alert(getComputedStyle(getById('node'),':before').display);
// alert(getComputedStyle(document.body,':before').display);
// function getErrorImg(imgs, pseudoElt) {
// if (imgs) {
// imgs = [].concat(imgs);
// } else {
// imgs = getByTag('img')
// }
// pseudoElt = pseudoElt || ':before';
// console.log(ret);
// var ret = [];
// for (var i = 0, img; img = imgs[i++];) {
// var style = getComputedStyle(img, pseudoElt);
// var display = style.display;
// if (display === 'flex') { // chrome / firefox / opera
// ret.push(img);
// } else if (display === 'inline-block') { // safari
// ret.push(img);
// } else if (style.content.trim()) {
// ret.push(img);
// }
// }
// return ret;
// }
// console.log(getErrorImg());
// console.log(null.toString()); // 触发上报
// console.error('查询错误');
// console.warn('id不应该为空');
return imager;
});
.swipe-overlay{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
// background-color: rgba(51,51,51,1);
background-color: #333;
z-index: -1;
opacity: 0;
transition: all .2s ease-in-out;
overflow: hidden;
}
.swipe-overlay-show{
z-index: 9999;
// background-color: rgba(51,51,51,1);
opacity: 1;
}
.swipe-overlay ul{
width: 100%;
height: 100%;
overflow: hidden;
}
.swipe-overlay ul li{
display: flex;
height: 100%;
width: 100%;
justify-content:center;
align-items:center;
overflow: hidden;
position:absolute;
}
// .swipe-overlay ul li img{
// width: auto;
// height: auto;
// }
.swipe-overlay-index-and-length{
position: fixed;
font-size: 12px;
color:#fff;
z-index: 10000;
bottom: 12px;
left: 12px;
width: 100%;
}