Commit 8970e6db authored by luye's avatar luye
Browse files

Initial commit

parents
No related merge requests found
Showing with 501 additions and 0 deletions
+501 -0
api.js 0 → 100644
import Vue from 'vue'
import Viewer from 'viewerjs'
import { assign } from './util'
const api = ({ images = [], options = {} } = {}) => {
options = assign(options, {
inline: false, // 只能使用modal模式
})
// 创建存放viewerjs加载所需图片的占位元素,无需实际展现
const ViewerToken = Vue.extend({
render(h) {
return h(
'div',
{
style: {
display: 'none',
},
class: ['__viewer-token'],
},
images.map((attr) => {
return h(
'img',
{
attrs: typeof attr === 'string' ? { src: attr } : attr,
},
)
}),
)
},
})
const token = new ViewerToken()
token.$mount()
document.body.appendChild(token.$el)
// 加载Viewer
const $viewer = new Viewer(token.$el, options)
const $destroy = $viewer.destroy.bind($viewer)
$viewer.destroy = function() {
$destroy()
token.$destroy()
document.body.removeChild(token.$el)
return $viewer
}
$viewer.show()
// 关闭Viewer模态窗口时,销毁token
token.$el.addEventListener('hidden', function() {
if (this.viewer === $viewer) {
$viewer.destroy()
}
})
return $viewer
}
export default api
<template>
<div>
<slot :images="images" :options="option">
</slot>
</div>
</template>
<script>
import Viewer from 'viewerjs'
export default {
data() {
return {
option: {}
}
},
created() {
this.init()
},
props: {
images: {
type: Array,
},
rebuild: {
type: Boolean,
default: false,
},
trigger: {},
options: {
type: Object,
},
},
computed: {
// option2(){
// return this.option
// }
},
watch: {
images() {
this.$nextTick(() => {
this.onChange()
})
},
trigger: {
handler() {
this.$nextTick(() => {
this.onChange()
})
},
deep: true,
},
options: {
handler() {
this.$nextTick(() => {
this.rebuildViewer()
})
},
deep: true,
},
},
mounted() {
this.createViewer()
},
unmounted() {
this.destroyViewer()
},
methods: {
init() {
Object.assign(this.option, this.options)
Object.assign(this.option, {show: this.show})
},
show() {
let container = document.getElementsByClassName('viewer-container')[0]
let reg = /.+({.+}).+/
let viewBigBtn = document.createElement('div')
viewBigBtn.innerText = '查看大图'
viewBigBtn.className = 'xnwBtn viewBig'
viewBigBtn.addEventListener('click', _ => {
let url = window.decodeURIComponent(container.getElementsByClassName('viewer-canvas')[0].getElementsByTagName('img')[0].src)
let fileid = url.match(reg)[1]
let bigUrl = `//cdn.xnwimg.com/down/${fileid}/1.jpg`
window.open(bigUrl, '_blank')
})
container.appendChild(viewBigBtn)
if (this.options && this.options.correct) {
let correctBtn = document.createElement('div')
correctBtn.innerText = '批注'
correctBtn.className = 'xnwBtn correct'
correctBtn.addEventListener('click', _ => {
let url = window.decodeURIComponent(container.getElementsByClassName('viewer-canvas')[0].getElementsByTagName('img')[0].src)
let fileid = url.match(reg)[1]
this.$emit('correct', fileid)
this.rebuildViewer()
})
container.appendChild(correctBtn)
}
},
onChange() {
if (this.rebuild) {
this.rebuildViewer()
}
else {
this.updateViewer()
}
},
rebuildViewer() {
this.destroyViewer()
this.createViewer()
},
updateViewer() {
if (this.$viewer) {
this.$viewer.update()
this.$emit('inited', this.$viewer)
}
else {
this.createViewer()
}
},
destroyViewer() {
this.$viewer && this.$viewer.destroy()
},
createViewer() {
this.init()
this.$viewer = new Viewer(this.$el, this.option)
this.$emit('inited', this.$viewer)
},
},
}
</script>
<style lang="scss">
.viewer-container {
.xnwBtn {
background-color: orange;
border-radius: 2px;
cursor: pointer;
height: 28px;
line-height: 28px;
color: white;
font-size: 14px;
overflow: hidden;
position: absolute;
right: 100px;
top: 40px;
padding: 0 7px;
transition: background-color 0.15s;
&.viewBig {
right: 100px;
}
&.correct {
right: 200px;
}
&:hover {
opacity: .9;
}
}
}
</style>
import Vue from 'vue'
import Viewer from 'viewerjs'
import debounce from 'lodash/debounce'
const directive = ({ name = 'viewer', debug = false } = {}) => {
function createViewer(el, options, rebuild = false, observer = false) {
// nextTick执行,否则可能漏掉未渲染完的子元素
Vue.nextTick(() => {
// 如果无图片或者和上次比较没有变化,那么就没有必要重新初始化或更新
if (observer && !imageDiff(el)) return
if (rebuild || !el[`$${name}`]) {
destroyViewer(el)
el[`$${name}`] = new Viewer(el, options)
log('Viewer created')
}
else {
el[`$${name}`].update()
log('Viewer updated')
}
})
}
function imageDiff(el) {
const imageContent = el.innerHTML.match(/<img([\w\W]+?)[\\/]?>/g)
const viewerImageText = imageContent ? imageContent.join('') : undefined
if (el.__viewerImageDiffCache === viewerImageText) {
log('Element change detected, but image(s) has not changed')
return false
}
else {
log('Image change detected')
el.__viewerImageDiffCache = viewerImageText
return true
}
}
function createObserver(el, options, debouncedCreateViewer, rebuild) {
destroyObserver(el)
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver
if (!MutationObserver) {
log('Observer not supported')
return
}
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
log(`Viewer mutation:${mutation.type}`)
debouncedCreateViewer(el, options, rebuild, true)
})
})
const config = { attributes: true, childList: true, characterData: true, subtree: true }
observer.observe(el, config)
el.__viewerMutationObserver = observer
log('Observer created')
}
function createWatcher(el, { expression }, vnode, debouncedCreateViewer) {
const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
if (!expression || !simplePathRE.test(expression)) {
log('Only simple dot-delimited paths can create watcher')
return
}
el.__viewerUnwatch = vnode.context.$watch(expression, (newVal, oldVal) => {
log('Change detected by watcher: ', expression)
debouncedCreateViewer(el, newVal, true)
}, {
deep: true,
})
log('Watcher created, expression: ', expression)
}
function destroyViewer(el) {
if (!el[`$${name}`]) {
return
}
el[`$${name}`].destroy()
delete el[`$${name}`]
log('Viewer destroyed')
}
function destroyObserver(el) {
if (!el.__viewerMutationObserver) {
return
}
el.__viewerMutationObserver.disconnect()
delete el.__viewerMutationObserver
log('Observer destroyed')
}
function destroyWatcher(el) {
if (!el.__viewerUnwatch) {
return
}
el.__viewerUnwatch()
delete el.__viewerUnwatch
log('Watcher destroyed')
}
function log() {
debug && console.log(...arguments)
}
const directive = {
bind(el, binding, vnode) {
log('Viewer bind')
const debouncedCreateViewer = debounce(createViewer, 50)
debouncedCreateViewer(el, binding.value)
// 创建watch监听options表达式变化
createWatcher(el, binding, vnode, debouncedCreateViewer)
// 是否监听dom变化
if (!binding.modifiers.static) {
// 增加dom变化监听
createObserver(el, binding.value, debouncedCreateViewer, binding.modifiers.rebuild)
}
},
unbind(el, binding) {
log('Viewer unbind')
// 销毁dom变化监听
destroyObserver(el)
// 销毁指令表达式监听
destroyWatcher(el)
// 销毁viewer
destroyViewer(el)
},
}
return directive
}
export default directive
index.js 0 → 100644
import Viewer from 'viewerjs'
import { assign } from './util'
import component from './component.vue'
import directive from './directive'
import api from './api'
export {
component,
directive,
api,
Viewer,
}
export default {
install(Vue, { name = 'viewer', debug = false, defaultOptions } = {}) {
Viewer.setDefaults(defaultOptions)
Vue.component(name, assign(component, { name }))
Vue.directive(name, directive({ name, debug }))
Vue.prototype[`$${name}Api`] = api
},
setDefaults(defaultOptions) {
Viewer.setDefaults(defaultOptions)
},
}
util.js 0 → 100755
export const inBrowser = typeof window !== 'undefined' && window !== null
export const hasIntersectionObserver = checkIntersectionObserver()
const isEnumerable = Object.prototype.propertyIsEnumerable
const getSymbols = Object.getOwnPropertySymbols
/**
* is object
*
* @param {*} val
* @returns {boolean}
*/
export function isObject(val) {
return typeof val === 'function' || toString.call(val) === '[object Object]'
}
/**
* is primitive
*
* @param {*} val
* @returns {boolean}
*/
export function isPrimitive(val) {
return typeof val === 'object' ? val === null : typeof val !== 'function'
}
/**
* check private key
*
* @export
* @param {*} key
* @returns {boolean}
*/
export function isValidKey(key) {
return key !== '__proto__' && key !== 'constructor' && key !== 'prototype'
}
/**
* Check if IntersectionObserver can be used
*
* @returns {boolean}
*/
export function checkIntersectionObserver() {
if (inBrowser
&& 'IntersectionObserver' in window
&& 'IntersectionObserverEntry' in window
&& 'intersectionRatio' in window.IntersectionObserverEntry.prototype) {
// Minimal polyfill for Edge 15's lack of `isIntersecting`
// See: https://github.com/w3c/IntersectionObserver/issues/211
if (!('isIntersecting' in window.IntersectionObserverEntry.prototype)) {
Object.defineProperty(window.IntersectionObserverEntry.prototype,
'isIntersecting', {
get() {
return this.intersectionRatio > 0
},
})
}
return true
}
return false
}
/**
* Assign the enumerable es6 Symbol properties from one
* or more objects to the first object passed on the arguments.
* Can be used as a supplement to other extend, assign or
* merge methods as a polyfill for the Symbols part of
* the es6 Object.assign method.
* https://github.com/jonschlinkert/assign-symbols
*
* @param {*} target
* @param {...any[]} args
* @returns
*/
function assignSymbols(target, ...args) {
if (!isObject(target)) {
throw new TypeError('expected the first argument to be an object')
}
if (args.length === 0 || typeof Symbol !== 'function' || typeof getSymbols !== 'function') {
return target
}
for (const arg of args) {
const names = getSymbols(arg)
for (const key of names) {
if (isEnumerable.call(arg, key)) {
target[key] = arg[key]
}
}
}
return target
}
/**
* Deeply assign the values of all enumerable-own-properties and symbols
* from one or more source objects to a target object. Returns the target object.
* https://github.com/jonschlinkert/assign-deep
*
* @param {*} target
* @param {...any[]} args
* @returns
*/
export function assign(target, ...args) {
let i = 0
if (isPrimitive(target)) target = args[i++]
if (!target) target = {}
for (; i < args.length; i++) {
if (isObject(args[i])) {
for (const key of Object.keys(args[i])) {
if (isValidKey(key)) {
if (isObject(target[key]) && isObject(args[i][key])) {
assign(target[key], args[i][key])
}
else {
target[key] = args[i][key]
}
}
}
assignSymbols(target, args[i])
}
}
return target
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment