import { BaseAbstract } from '../utils/BaseAbstract'; import getBackData from '../utils/getBackData'; import { IKeyValue } from '../interface/IKeyValue.interface'; import { IImgClipConfig, IImgSrcData, IImgSizeData, ICurImgSize } from '../interface/IImgClipConfig.interface'; import { DEFAULT_CONFIG } from '../constants'; /** * 裁剪图片的url * @exports * @class ImgClip */ export class ImgClip extends BaseAbstract { /** * 默认配置选项 */ protected defaultOption = DEFAULT_CONFIG; /** * 构造方法 * @param {IImgClipConfig} options 选项参数 */ constructor (options: IImgClipConfig) { super(options); const _that = this; _that.setDefaultOptions(options); const _options = _that.defaultOption; if (!_options.contWidth) { let _w: number = window.innerWidth; _options.contWidth = Math.min(_options.maxWidth || _w, _w); } if (!_options.contHeight) { let _h: number = window.innerHeight; _options.contHeight = Math.min(_options.maxHeight || _h, _h); } } /** * 裁剪图片 * @param {String} src 图片url地址 * @param {IImgClipConfig} options 选项参数 * @returns {Promise} * @public */ public getSrc (src: string, options?: IImgClipConfig): Promise { const _that = this; const _options: IImgClipConfig = _that.getOptions(options || {}); src = (src || '') + ''; return new Promise((resolve, reject) => { getBackData(_options.ratio, _options).then((ratio: any) => { resolve(_that._getSrc(src, ratio, _options)); }, reject); }); } /** * 裁剪图片 * @param {String} src 图片的url地址 * @param {Number} ratio 图片的缩放比 * @param {IImgClipConfig} options 选项参数 * @returns {IImgSrcData} * @private */ private _getSrc (src: string, ratio: number, options: IImgClipConfig): IImgSrcData|IKeyValue { const _that = this; const _options: IImgClipConfig = _that.getOptions(options); src = (src || '') + ''; if (!src) { return {}; } let _size: IImgSizeData = _that._getSize(src, _options); // 在没有去除@后缀之前获取大小 src = src.replace(/@.*/g, ''); let _oldSrc = src; let _matchArr = src.match(/\.([^.]+)$/); let _extFix = (_matchArr && _matchArr[1]) || ''; let _oldExtFix = _extFix; _extFix = (_extFix && _options.isSupportWebp) ? 'webp' : (_extFix ? 'png' : _extFix); let _clipCode = _that._getClipCode(_size, ratio, _options); src = _that._genSrc(src, _clipCode, _extFix); return Object.assign({}, _size, { src: src || _oldSrc, cutterSrc: src || _oldSrc, oldSrc: _oldSrc, extFix: _extFix, oldExtFix: _oldExtFix }); } /** * 生成新的src地址 * @param {String} src src地址 * @param {String} clipCode 裁剪码 * @param {String} extFix 图片后缀 * @returns {String} * @private */ private _genSrc (src: string, clipCode: string, extFix: string): string { src = (src || '') + ''; if (!src) { return src; } let _suffix: string = ''; if (clipCode) { if (extFix === 'gif') { _suffix = '?@' + clipCode + '99q.src'; } else { _suffix = '@' + clipCode + '99q.' + extFix; } } else { _suffix = '@.' + extFix; } return (src + _suffix).replace(/https?/, 'https'); } /** * 获取图片的裁剪码 * @param {IImgSizeData} size 图片的大小 * @param {Number} ratio 图片裁剪比 * @param {IImgClipConfig} options 选项参数 * @returns {String} * @private */ private _getClipCode (size: IImgSizeData, ratio: number, options: IImgClipConfig): string { const _that = this; if (!size) { return ''; } let _res: string = ''; const _options: IImgClipConfig = _that.getOptions(options); let _width: number|undefined = size.width; let _height: number|undefined = size.height; const _scale: number = (_width || 0) / (_height || 1); const _imgScale: number = size.scale || 1; const _cutter: any = _options.cutter || 'auto'; switch (_cutter) { case 'contain': // 最大的一边铺满父容器 if (_imgScale >= _scale) { _height = 0; } else { _width = 0; } break; case 'cover': // 最小的一边铺满父容器 if (_imgScale <= _scale) { _height = 0; } else { _width = 0; } break; } ratio = ratio || 1; if (_height) { let _h = _ceil(_ceil(_height) * ratio); _h = Math.min(size.imgHeight || _h, _h); _res = `${_h}h_`; } else if (_width) { let _w = _ceil(_ceil(_width) * ratio); _w = Math.min(size.imgWidth || _w, _w); _res = `${_w}w_`; } return _res; /** * 向上取整 * @param {Number} size 大小 * @returns {Number} */ function _ceil (size: number): number { return Math.ceil(size / 10) * 10; } } /** * 获取图片的大小 * @param {string} src 图片的src地址 * @param {IImgClipConfig} options 选项参数 * @returns {IImgSizeData} * @private */ private _getSize(src: string, options: IImgClipConfig): IImgSizeData { const _that = this; const _options = _that.getOptions(options); let _res: IImgSizeData = {}; const _cutter = _options.cutter || 'auto'; let _wEl: any = _options.width; let _hEl: any = _options.height; let _elW: number|undefined; let _elH: number|undefined; src = (src || '') + ''; if (!src || (!_wEl && !_hEl)) { return _res; } if ((!_hEl || _hEl instanceof HTMLElement) && _wEl instanceof HTMLElement) { _elW = _wEl.clientWidth; } if ((!_elW || _cutter !== 'auto') && (!_wEl || _wEl instanceof HTMLElement) && _hEl instanceof HTMLElement) { _elH = _hEl.clientHeight; } if (_elW || _elH) { _wEl = (_elW && (_elW + 'px')) || ''; _hEl = (_elH && (_elH + 'px')) || ''; } if (!_wEl && !_hEl) { return _res; } _wEl = _that._toPx(_wEl, _options); _hEl = _that._toPx(_hEl, _options, true); _res = _that._getImgSize(src); if (_res.imgWidth || _res.imgHeight || _options.forceCut) { const _scale: number = _res.scale || 1; if (_wEl) { _res.width = _wEl; } if (_hEl) { _res.height = _hEl; } if (!_wEl && _hEl) { _res.width = _hEl * _scale; } if (!_hEl && _wEl) { _res.height = _wEl / _scale; } if (_res.width) { _res.width = _that._filterNum(_res.width); } if (_res.height) { _res.height = _that._filterNum(_res.height); } } return _res; } /** * 获取图片的大小 * @param {string} src 图片地址 * @returns {ICurImgSize} * @private */ private _getImgSize (src: string): ICurImgSize { const _that = this; let _res: ICurImgSize = {}; src = (src || '') + ''; if (!src) { return _res; } let _match = src.match(/([0-9]+)w_([0-9]+)h/); if (!_match) { _match = src.match(/w([0-9]+)_h([0-9]+)/); } if (_match && _match[1] && _match[2]) { let _w = _that._filterNum(_match[1]); let _h = _that._filterNum(_match[2]); _res = { imgWidth: _w, imgHeight: _h, scale: _w / _h }; } return _res; } /** * 将大小转换成px的值 * @param {String} size 大小 * @param {IImgClipConfig} options 选项 * @param {Boolean} isHeight 是否为高度 * @param {Boolean} noOffset 是否没有偏移量 * @returns {Number|undefined} * @private */ private _toPx (size: string|number, options: IImgClipConfig, isHeight?: boolean, noOffset?: boolean): number|undefined { const _that = this; const _options: IImgClipConfig = _that.getOptions(options); let _res: number|undefined; size = size + ''; if (!size) { return _res; } let _match = size.match(/\s*(\d+\.?\d*)(%|[a-zA-Z]*)?\s*/); if (_match && _match[1]) { let _size = _that._filterNum(_match[1]); let _unit = (_match[2] || '').toLocaleLowerCase(); switch (_unit) { case 'px': _res = _size; break; case '%': case 'vw': case 'vh': if (_unit === 'vh') { isHeight = true; } let _base: number|undefined = 0; if (isHeight) { _base = _options.contHeight || window.innerHeight; if (_options.contHeightOffset && !noOffset) { _base = _base - (_that._toPx(_options.contHeightOffset, { contHeight: _base }, true, true) || 0); } } else { _base = _options.contWidth || window.innerWidth; if (_options.contWidthOffset && !noOffset) { _base = _base - (_that._toPx(_options.contWidthOffset, { contWidth: _base }, false, true) || 0); } } _res = (_size / 100) * _base; break; default: _res = _size * (_options.remRoot || 20); break; } } return _res; } /** * 过滤数字 * @param {String|Number} str 需要过滤的数字 * @returns {Number} * @private */ private _filterNum (str: string|number): number { let _res: number = 0; try { _res = parseFloat(str + ''); } catch (err) { _res = 0; } return _res; } } /** * 实例化工厂方法 * @param {IImgClipConfig} options 选项参数 * @returns {ImgClip} */ export default function (options: IImgClipConfig): ImgClip { return ImgClip.instance(options); } export { IImgClipConfig, IImgSizeData, ICurImgSize, IImgSrcData // tslint:disable-next-line };