import './style.less' import classnames from 'classnames' import React, { PureComponent } from 'react' import { PAD_RATIO, PHONE_RATIO } from '../common' import { loadImage } from '../utils' interface ImgItem { src: string height: number width?: number divInner?: boolean } export interface Props { list?: Array img?: ImgItem useDivInner?: boolean pxToRem?: boolean className?: string isPad?: boolean width?: number replace?: boolean onClick?: (e: any, imgItem: ImgItem, index: number) => void onSuccess?: () => void onFailure?: () => 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 Picture extends PureComponent { public static defaultProps = { pxToRem: true } public container: HTMLDivElement | null = null public dpr = getDPR() public componentDidMount() { this.initImage() } public componentWillUnmount() { this.container = null } public initImage() { const { img, list, onSuccess, onFailure } = this.props const imgList = img ? [img] : list let sequence: any = Promise.resolve() ;(imgList as Array).forEach((listItem: ImgItem, index: number) => { sequence = sequence.then(() => { return this.createImage(listItem, index) }) }) sequence.then(onSuccess, onFailure) } public createImage(listItem: ImgItem, index: number) { const { dpr, props, container } = this const { onClick, useDivInner, pxToRem, isPad, width: superWidth, replace } = props const { width: itemWidth, height, divInner, src } = listItem const isDivInner = typeof divInner === 'undefined' ? useDivInner : divInner const ratio = isPad ? PAD_RATIO : PHONE_RATIO const heightString = pxToRem ? `${height / ratio}rem` : `${height}px` const width = itemWidth || superWidth const widthString = width && pxToRem ? `${width / ratio}rem` : `${width}px` return new Promise((resolve, reject) => { if (!container) return const box = document.createElement('div') box.className = 'imager-container' box.style.height = heightString if (widthString) box.style.width = widthString container.appendChild(box) if (onClick) { box.addEventListener('click', event => { onClick(event, listItem, index) }) } const nodeType = isDivInner ? 'div' : 'img' const imgEl = document.createElement(nodeType) imgEl.className = 'imager-inner' const resource = new Image() const miniImg = `${src}?imageView2/2/h/${height}` const suitableImg = dpr === 3 ? src : `${src}?imageView2/2/h/${height * dpr}` resource.onload = () => { box.style.backgroundImage = `url(${miniImg})` const suitableImgLoader = replace ? loadImage(suitableImg) : Promise.resolve({}) suitableImgLoader.then(() => { if (isDivInner) { imgEl.style.backgroundImage = `url(${suitableImg})` } else { const el = imgEl as HTMLImageElement el.src = suitableImg el.style.height = heightString if (widthString) el.style.width = widthString } if (box) box.appendChild(imgEl) if (replace) box.style.backgroundImage = '' }) resolve() } resource.onerror = err => { reject(err) } resource.src = miniImg }) } public render() { const { className, isPad } = this.props return (
(this.container = el)} className={classnames('ph-picture', className, { 'is-pad': isPad })} /> ) } }