import './style.less' import classnames from 'classnames' import * as React from 'react' import { LONG_TOUCH_DURATION, MAX_MOVE, PAD_WIDTH_LIST, PHONE_WIDTH_LIST } from '../common' interface ImgItem { [propName: string]: any } export interface Props { className?: string widthList?: Array isPad?: boolean imgList: Array useDivInner?: boolean onClick?: (e: any, imgItem: ImgItem, index: number) => void onSuccess: () => void onFailure: () => void onLongTouch?: () => void } function getDPR() { let dpr = Math.floor(window.devicePixelRatio) || 1 if (dpr > 3) dpr = 3 if (dpr < 1) dpr = 1 return dpr } export default class Imager extends React.Component { public static defaultProps = { className: '', onSuccess() {}, onFailure() {} } public dpr = getDPR() public container: HTMLDivElement | null = null private touchTimer: Number | null = null private touchX = 0 private touchY = 0 public componentDidMount() { this.initImage() } public initImage() { const { imgList, onSuccess, onFailure } = this.props let sequence = Promise.resolve() imgList.forEach((listItem, index) => { sequence = sequence.then(() => { return this.createImage(listItem, index) }) }) sequence.then(onSuccess, onFailure) } public createImage(listItem: ImgItem, index: number): Promise { const { dpr, props, container } = this const { widthList, onClick, useDivInner, isPad } = props const { height, divInner } = listItem const isDivInner = typeof divInner === 'undefined' ? useDivInner : divInner return new Promise((resolve, reject) => { const box = document.createElement('div') box.className = 'imager-container' if (height) box.style.height = height if (container) container.appendChild(box) const nodeType = isDivInner ? 'div' : 'img' const imgEl = document.createElement(nodeType) imgEl.className = 'imager-inner' if (onClick) { imgEl.addEventListener('click', event => { onClick(event, listItem, index) }) } const list = widthList && widthList.length ? widthList : isPad ? PAD_WIDTH_LIST : PHONE_WIDTH_LIST const resource = new Image() const miniImg = listItem[list[0]] const suitableImg = listItem[list[dpr - 1]] resource.onload = () => { box.style.backgroundImage = `url(${miniImg})` box.style.backgroundSize = `100% 100%` if (isDivInner) { imgEl.style.backgroundImage = `url(${suitableImg})` } else { const el = imgEl as HTMLImageElement el.src = suitableImg el.style.height = height } box.appendChild(imgEl) resolve() } resource.onerror = err => { reject(err) } resource.src = miniImg }) } public onTouchStart = (e: React.TouchEvent) => { const { pageX, pageY } = e.touches[0] this.touchX = pageX this.touchY = pageY this.touchTimer = Date.now() } public onTouchEnd = (e: React.TouchEvent) => { const touchDuration = Date.now() - (this.touchTimer as number) const { pageX, pageY } = e.changedTouches[0] const { onLongTouch } = this.props const { touchX, touchY } = this const isMove = pageX - touchX > MAX_MOVE || touchX - pageX > MAX_MOVE || pageY - touchY > MAX_MOVE || touchY - pageY > MAX_MOVE if (touchDuration > LONG_TOUCH_DURATION && onLongTouch && !isMove) { onLongTouch() } } public render() { const { className, isPad } = this.props return (
(this.container = el)} className={classnames('ph-imager', className, { 'is-pad': isPad })} /> ) } }