UNPKG

kui-vue

Version:

A high quality UI Toolkit built on Vue.js 2.0

333 lines (329 loc) 11.3 kB
import Icon from '../icon' import transfer from "../_tool/transfer"; import { getChild, measureScrollBar } from '../_tool/utils' let cacheBodyOverflow = {}; import { Refresh, Close, ArrowDown, IconImage, ChevronUp, Sync, AddCircleOutline, RemoveCircleOutline } from 'kui-icons' export default { name: 'Preview', directives: { transfer }, props: { type: String, origin: String, hasControl: Boolean, value: Boolean, transfer: { type: Boolean, default: false }, data: { type: Array, default: () => [] }, showSwitch: Boolean, showPanel: Boolean, globle: { type: Boolean, default: true }, }, data() { return { scale: 1, rotate: 0, startPos: { x: 0, y: 0 }, initPos: { x: 0, y: 0 }, left: 0, top: 0, ismousedown: false, visible: this.value, src: this.origin, loading: false, error: false, vertical: true, isShowPanel: this.showPanel, panelRight: 0, touch: false, } }, watch: { origin(src) { this.src = src }, value(show) { this.visible = show this.resetBodyStyle(show) if (show) { this.$nextTick(() => { this.updatePanelRight() }) } }, src(src) { if (this.type == 'media') return let img = new Image() this.loading = true this.error = false img.onload = () => { this.loading = false img = null } img.onerror = () => { this.loading = false img = null this.error = true } img.src = src // } }, showPanel(value) { this.isShowPanel = value this.updatePanelRight() } }, methods: { updatePanelRight() { let panel = this.$refs.panelRef this.panelRight = panel && this.isShowPanel ? panel.offsetWidth : 0 }, setRotate(left) { let { rotate } = this rotate = left ? (rotate - 90) : (rotate + 90); this.vertical = !this.vertical this.rotate = rotate this.resetPosition() }, setScale(zoom) { let { scale } = this scale = zoom ? (scale + 1) : (scale - 1); scale = zoom ? Math.min(scale, 5) : Math.max(1, scale) this.scale = scale this.resetPosition() }, close() { this.visible = false // this.scale = 1 // this.rotate = 0 this.$emit('input', false) this.$emit('close') }, mousewheel(e) { let { deltaY } = e this.setScale(deltaY && deltaY < 0) e.stopPropagation() e.preventDefault() }, mousedown(e) { if (this.$refs.imgRef && this.$refs.imgRef.contains(e.target)) { if (e.button && e.button != 0) return; let clientX, clientY; if (e.touches && e.touches.length == 1) { clientX = e.touches[0].clientX clientY = e.touches[0].clientY } else { clientX = e.clientX clientY = e.clientY } this.ismousedown = true this.startPos = { x: clientX, y: clientY } this.initPos = { x: clientX, y: clientY } this.mousemove(e) let [e1, e2] = this.touch ? ['touchmove', 'touchend'] : ['mousemove', 'mouseup'] document.addEventListener(e1, this.mousemove, { passive: false }) document.addEventListener(e2, this.mouseup, { passive: false }) } }, resetPosition() { if (this.error) return; let { innerHeight, innerWidth } = window let { $refs, scale, top, left, vertical } = this let { offsetWidth, offsetHeight } = $refs.imgRef let panelWidth = $refs.panelRef && this.isShowPanel ? $refs.panelRef.offsetWidth : 0 let newWidth = offsetWidth + '' let newHeight = offsetHeight + '' if (!vertical) { newWidth = offsetHeight + '' newHeight = offsetWidth + '' } if (newWidth * scale >= (innerWidth - panelWidth)) { let maxLeft = (newWidth * scale - (innerWidth - panelWidth)) / 2 if (left >= maxLeft) { this.left = maxLeft } else if (this.left < -maxLeft) { this.left = -maxLeft } } else { this.left = 0 } if (newHeight * scale >= innerHeight) { let maxTop = (newHeight * scale - innerHeight) / 2 if (top >= maxTop) { this.top = maxTop } else if (top < -maxTop) { this.top = -maxTop } } else { this.top = 0 } }, mouseup(e) { this.ismousedown = false this.resetPosition() let [e1, e2] = this.touch ? ['touchmove', 'touchend'] : ['mousemove', 'mouseup'] document.removeEventListener(e1, this.mousemove) document.removeEventListener(e2, this.mouseup) }, mousemove(e) { if (this.ismousedown) { e.preventDefault() let clientX, clientY; if (e.touches && e.touches.length == 1) { clientX = e.touches[0].clientX clientY = e.touches[0].clientY } else { clientX = e.clientX clientY = e.clientY } let { x, y } = this.startPos this.left += clientX - x this.top += clientY - y this.startPos = { x: clientX, y: clientY } } }, switchImage(left) { this.scale = 1 let { data = [], src } = this let index = data.indexOf(src), i = index + 0; index = left ? (index - 1) : (index + 1) index = Math.max(0, index) index = Math.min(index, data.length - 1) if (this.globle && !this.$slots.panel) { this.src = data[index] } if ((left && i == 0) || (!left && i == data.length - 1)) return; this.$emit('switch', index) }, download() { if (!this.error) { var x = new XMLHttpRequest(); x.open("GET", this.src, true); x.responseType = 'blob'; x.onload = function (e) { var url = window.URL.createObjectURL(x.response) var a = document.createElement('a'); a.href = url a.download = '' a.click() } x.send(); // window.open(this.src) } }, resetBodyStyle(opened) { let target = document.body if (!this.show && !cacheBodyOverflow.hasOwnProperty('overflow')) { cacheBodyOverflow = { width: target.style.width, overflow: target.style.overflow, overflowX: target.style.overflowX, overflowY: target.style.overflowY, } } if (opened) { let barWidth = measureScrollBar(true) let hasBar = target.scrollHeight > target.clientHeight || target.offsetHeight > target.clientHeight if (barWidth && hasBar) { target.style.width = `calc(100% - ${barWidth}px)` target.style.overflow = `hidden` } } else { setTimeout(() => { Object.keys(cacheBodyOverflow).forEach(key => { target.style[key] = cacheBodyOverflow[key] || '' delete cacheBodyOverflow[key] }) }, 300) } }, togglePanel() { this.isShowPanel = !this.isShowPanel this.$emit('toggle-panel', this.isShowPanel) this.$nextTick(() => this.resetPosition()) this.updatePanelRight() }, getPanel() { let panel = getChild(this.$slots.panel) if (panel.length) { return <div class={["k-image-preview-panel", { 'k-image-preview-panel-hidden': !this.isShowPanel }]} ref="panelRef"> <span class="k-image-preview-panel-action" onClick={() => this.togglePanel()}><Icon type={ChevronUp} /></span> {panel} </div> } return null } }, beforeDestroy() { document.removeEventListener('mousewheel', this.mousewheel) }, mounted() { if (!this.$isServer) { let touch = !!(('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch) this.touch = touch let even = touch ? 'touchstart' : 'mousedown' document.addEventListener(even, this.mousedown, { passive: false }) document.addEventListener('mousewheel', this.mousewheel, { passive: false }) } }, render(h) { const { scale, rotate, visible, src, left, top, transfer, showSwitch, data, loading, panelRight, type } = this const imgStyle = { transform: `scale3d(${scale}, ${scale}, 1) rotate(${rotate}deg)` } const moveStyle = { transform: `translate3d(${left}px, ${top}px, 0px)`, transition: this.ismousedown ? '0s' : null } const imgPorps = { class: 'k-image-preview-img', attrs: { src }, style: imgStyle, ref: "imgRef", } let tools = getChild(this.$slots.tool) return <div class="k-image-preview-root" v-transfer={transfer}> <transition name="k-image-zoom"> <div class="k-image-preview" v-show={visible} > <div class="k-image-preview-mask" onClick={this.close}></div> <div class="k-image-preview-wrap" style={{ right: panelRight + 'px' }}> <ul class="k-image-preview-control"> <li class="k-image-preview-action" onClick={this.close}><Icon type={Close} /></li> <li class="k-image-preview-action-divider" /> { tools.map(tool => { return <li class="k-image-preview-action">{tool}</li> }) } <li class="k-image-preview-action" onClick={this.download}><Icon type={ArrowDown} /></li> <li class={["k-image-preview-action", { 'k-image-preview-action-disabled': scale >= 5 }]} onClick={() => this.setScale(1)}><Icon type={AddCircleOutline} /></li> <li class={["k-image-preview-action", { 'k-image-preview-action-disabled': scale <= 1 }]} onClick={() => this.setScale(0)}><Icon type={RemoveCircleOutline} /></li> <li class="k-image-preview-action k-image-preview-action-rotate-right" onClick={() => this.setRotate(0)} ><Icon type={Refresh} /></li> <li class="k-image-preview-action k-image-preview-action-rotate-left" onClick={() => this.setRotate(1)} ><Icon type={Refresh} /></li> </ul> <div class="k-image-preview-img-wrap" style={moveStyle}> {type == 'media' ? <video controls {...imgPorps} /> : !this.error ? <img {...imgPorps} /> : <div class="k-image-preview-img-error"><Icon type={IconImage} /></div> } </div> {showSwitch ? [<div class={["k-image-preview-switch-left", { 'k-image-preview-switch-disabled': data.indexOf(src) == 0 }]} onClick={() => this.switchImage(1)}><Icon type={ChevronUp} /></div>, <div class={["k-image-preview-switch-right", { 'k-image-preview-switch-disabled': data.indexOf(src) == (data.length - 1) }]} onClick={() => this.switchImage()}><Icon type={ChevronUp} /></div>] : null} {loading ? <div class="k-image-preview-loading"><Icon type={Sync} spin /></div> : null} </div> {this.getPanel()} </div> </transition> </div> } }