|
@@ -0,0 +1,324 @@
|
|
|
+/**
|
|
|
+ * 兼容高分辨率屏幕高像素比的情况
|
|
|
+ *
|
|
|
+ * @param {*} context
|
|
|
+ */
|
|
|
+function getPixelRatio(context) {
|
|
|
+ let backingStore = context.backingStorePixelRatio ||
|
|
|
+ context.webkitBackingStorePixelRatio ||
|
|
|
+ context.mozBackingStorePixelRatio ||
|
|
|
+ context.msBackingStorePixelRatio ||
|
|
|
+ context.oBackingStorePixelRatio ||
|
|
|
+ context.backingStorePixelRatio || 1
|
|
|
+ return (window.devicePixelRatio || 1) / backingStore
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 使用指定画布,按照云阅卷拼接规则,将已加载好的图片拼接成新的图片对象
|
|
|
+ *
|
|
|
+ * @param {canvas} canvas
|
|
|
+ * @param {array} images
|
|
|
+ * @param {array} config
|
|
|
+ * @param {function} onSuccess
|
|
|
+ * @param {function} onError
|
|
|
+ */
|
|
|
+function mergeImages(canvas, images, config, onSuccess, onError) {
|
|
|
+ if (config == undefined || config.length == 0) {
|
|
|
+ //不指定拼接配置时,默认按顺序显示所有图片
|
|
|
+ config = []
|
|
|
+ for (let i = 0; i < images.length; i++) {
|
|
|
+ config.push({
|
|
|
+ i: i + 1,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ w: 0,
|
|
|
+ h: 0
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let maxWidth = 0
|
|
|
+ let totalHeight = 0
|
|
|
+ for (let i = 0; i < config.length; i++) {
|
|
|
+ //计算最大宽度与合计高度
|
|
|
+ if (config[i].w <= 0) {
|
|
|
+ config[i].w = images[config[i].i - 1].width
|
|
|
+ }
|
|
|
+ if (config[i].h <= 0) {
|
|
|
+ config[i].h = images[config[i].i - 1].height
|
|
|
+ }
|
|
|
+ maxWidth = Math.max(maxWidth, config[i].w)
|
|
|
+ totalHeight += config[i].h
|
|
|
+ }
|
|
|
+ let context = canvas.getContext("2d")
|
|
|
+ //获取设备像素比
|
|
|
+ //暂时只用原始尺寸,避免转换图片过大导致性能问题
|
|
|
+ //let ratio = getPixelRatio(context)
|
|
|
+ let ratio = 1
|
|
|
+ //设置画布大小
|
|
|
+ canvas.width = maxWidth * ratio
|
|
|
+ canvas.height = totalHeight * ratio
|
|
|
+ context.clearRect(0, 0, canvas.width, canvas.height)
|
|
|
+ context.fillStyle = "white";
|
|
|
+ context.fillRect(0, 0, canvas.width, canvas.height);
|
|
|
+ //绘画到画布
|
|
|
+ //高度位置与图片配置对应关系
|
|
|
+ let array = []
|
|
|
+ let top = 0
|
|
|
+ let height = 0
|
|
|
+ for (let i = 0; i < config.length; i++) {
|
|
|
+ context.drawImage(images[config[i].i - 1], config[i].x, config[i].y, config[i].w, config[i].h, 0, height, config[i].w * ratio, config[i].h * ratio)
|
|
|
+ height += config[i].h * ratio
|
|
|
+ top += config[i].h
|
|
|
+ array.push({
|
|
|
+ offsetY: top,
|
|
|
+ config: config[i]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ let image = new Image()
|
|
|
+ if (onSuccess) {
|
|
|
+ image.onload = function() {
|
|
|
+ onSuccess(image)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (onError) {
|
|
|
+ image.onerror = function() {
|
|
|
+ onError('image merge error')
|
|
|
+ }
|
|
|
+ }
|
|
|
+ image.config = array
|
|
|
+ // image.origin = {
|
|
|
+ // width: maxWidth,
|
|
|
+ // height: totalHeight
|
|
|
+ // }
|
|
|
+ image.src = canvas.toDataURL()
|
|
|
+ return image
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 针对云阅卷场景多图片处理工具
|
|
|
+ * 1. 支持指定服务器地址与是否强制刷新
|
|
|
+ * 2. 支持按云阅卷规则筛选实际需要加载的图片
|
|
|
+ * 3. 支持动态使用画布,按云阅卷规则垂直拼接出新的图片
|
|
|
+ *
|
|
|
+ * @param {*} option
|
|
|
+ */
|
|
|
+function ImageLoader(option) {
|
|
|
+ option = option || {}
|
|
|
+ this.server = option.server || ''
|
|
|
+ this.flush = option.flush === true
|
|
|
+}
|
|
|
+
|
|
|
+ImageLoader.prototype.load = function(urls, config, onSuccess, onError) {
|
|
|
+ let promises = []
|
|
|
+ let images = []
|
|
|
+ for (let i = 0; i < urls.length; i++) {
|
|
|
+ let url = this.server + urls[i] + (this.flush ? ('?' + new Date().getTime()) : '')
|
|
|
+ let image = new Image()
|
|
|
+ images.push(image)
|
|
|
+ promises.push(new Promise((resolve, reject) => {
|
|
|
+ if (this.contain(i + 1, config)) {
|
|
|
+ image.onload = function() {
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+ image.onerror = function() {
|
|
|
+ reject(url + ' load error')
|
|
|
+ }
|
|
|
+ image.crossOrigin = ''
|
|
|
+ image.src = url
|
|
|
+ } else {
|
|
|
+ resolve()
|
|
|
+ }
|
|
|
+ }))
|
|
|
+ }
|
|
|
+
|
|
|
+ Promise.all(promises).then(() => {
|
|
|
+ onSuccess ? onSuccess(images) : (function() {})
|
|
|
+ }).catch(error => {
|
|
|
+ onError ? onError(error) : (function() {})
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 判断指定序号的图片是否在拼接规则中存在,默认返回true
|
|
|
+ *
|
|
|
+ * @param {number} number
|
|
|
+ * @param {array} config
|
|
|
+ */
|
|
|
+ImageLoader.prototype.contain = function(number, config) {
|
|
|
+ if (config != undefined && config.length > 0) {
|
|
|
+ let find = false
|
|
|
+ for (let i = 0; i < config.length; i++) {
|
|
|
+ if (config[i].i == number) {
|
|
|
+ find = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return find
|
|
|
+ } else {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+ImageLoader.prototype.merge = function(urls, config, onSuccess, onError) {
|
|
|
+ if (this.canvas == undefined) {
|
|
|
+ this.canvas = document.createElement('canvas')
|
|
|
+ }
|
|
|
+ this.canvas.style = 'display:none'
|
|
|
+
|
|
|
+ let canvas = this.canvas
|
|
|
+ this.load(urls, config, function(images) {
|
|
|
+ mergeImages(canvas, images, config, onSuccess, onError)
|
|
|
+ }, onError)
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 针对画布的辅助工具类
|
|
|
+ * 1. 灵活调整大小与视网膜屏幕下的高清显示
|
|
|
+ * 2. 按照云阅卷的图片拼接规则绘制图片内容
|
|
|
+ *
|
|
|
+ * @param {} option
|
|
|
+ */
|
|
|
+function EasyCanvas(option) {
|
|
|
+ this.canvas = option.canvas
|
|
|
+ this.context = this.canvas.getContext("2d")
|
|
|
+ this.ratio = getPixelRatio()
|
|
|
+
|
|
|
+ this.originSize = {
|
|
|
+ width: 0,
|
|
|
+ height: 0
|
|
|
+ }
|
|
|
+ this.styleSize = {
|
|
|
+ width: 0,
|
|
|
+ height: 0
|
|
|
+ }
|
|
|
+ this.boxSize = {}
|
|
|
+ this.changeBoxSize(option.boxWidth, this.boxHeight)
|
|
|
+
|
|
|
+ this.fontConfig = {
|
|
|
+ name: option.fontName || 'Arial',
|
|
|
+ size: option.fontSize || 60,
|
|
|
+ color: option.fontColor || 'red'
|
|
|
+ }
|
|
|
+ this.lineConfig = {
|
|
|
+ width: option.lineWidth || 3,
|
|
|
+ color: option.lineColor || 'red'
|
|
|
+ }
|
|
|
+
|
|
|
+ this.onClick = option.onClick || (function() {})
|
|
|
+
|
|
|
+ let self = this
|
|
|
+ this.canvas.onclick = function(event) {
|
|
|
+ if (self.originSize.width > 0 && self.originSize.height > 0) {
|
|
|
+ self.onClick({
|
|
|
+ left: event.offsetX,
|
|
|
+ top: event.offsetY
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.changeBoxSize = function(width, height) {
|
|
|
+ if (width != undefined) {
|
|
|
+ this.boxSize.width = width
|
|
|
+ this.boxSize.height = undefined
|
|
|
+ } else if (height != undefined) {
|
|
|
+ this.boxSize.height = height
|
|
|
+ this.boxSize.width = undefined
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.clear = function() {
|
|
|
+ this.context.clearRect(0, 0, this.canvas.width, this.canvas.heigth)
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.reset = function(width, height) {
|
|
|
+ this.originSize.width = Math.max((width || 0), 0)
|
|
|
+ this.originSize.height = Math.max((height || 0), 0)
|
|
|
+ this.styleSize.width = this.originSize.width
|
|
|
+ this.styleSize.height = this.originSize.height
|
|
|
+
|
|
|
+ if (this.styleSize.width > 0 && this.styleSize.height > 0) {
|
|
|
+ if (this.boxSize.width != undefined) {
|
|
|
+ this.styleSize.width = Math.min(this.originSize.width, this.boxSize.width)
|
|
|
+ this.styleSize.height = this.styleSize.width * this.originSize.height / this.originSize.width
|
|
|
+ } else if (this.boxSize.height != undefined) {
|
|
|
+ this.styleSize.height = Math.min(this.originSize.height, this.boxSize.height)
|
|
|
+ this.styleSize.width = this.styleSize.height * this.originSize.width / this.originSize.height
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.canvas.width = this.styleSize.width * this.ratio
|
|
|
+ this.canvas.height = this.styleSize.height * this.ratio
|
|
|
+ this.canvas.style.width = this.styleSize.width + 'px'
|
|
|
+ this.canvas.style.height = this.styleSize.height + 'px'
|
|
|
+
|
|
|
+ this.clear()
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.drawImage = function(image, scale) {
|
|
|
+ this.drawImages([image], undefined, scale)
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.drawImages = function(images, config, scale) {
|
|
|
+ scale = scale || 1.0
|
|
|
+ if (config == undefined || config.length == 0) {
|
|
|
+ //不指定拼接配置时,默认按顺序显示所有图片
|
|
|
+ config = []
|
|
|
+ for (let i = 0; i < images.length; i++) {
|
|
|
+ config.push({
|
|
|
+ i: i + 1,
|
|
|
+ x: 0,
|
|
|
+ y: 0,
|
|
|
+ w: 0,
|
|
|
+ h: 0
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let maxWidth = 0;
|
|
|
+ let totalHeight = 0;
|
|
|
+ for (let i = 0; i < config.length; i++) {
|
|
|
+ //计算最大宽度与合计高度
|
|
|
+ if (config[i].w <= 0) {
|
|
|
+ config[i].w = images[config[i].i - 1].width;
|
|
|
+ }
|
|
|
+ if (config[i].h <= 0) {
|
|
|
+ config[i].h = images[config[i].i - 1].height;
|
|
|
+ }
|
|
|
+ maxWidth = Math.max(maxWidth, config[i].w * scale);
|
|
|
+ totalHeight += config[i].h * scale;
|
|
|
+ }
|
|
|
+ if (maxWidth > 0 && totalHeight > 0) {
|
|
|
+ //设置画布大小
|
|
|
+ this.reset(maxWidth, totalHeight)
|
|
|
+ //绘画到画布
|
|
|
+ scale = scale * this.styleSize.width / this.originSize.width
|
|
|
+ let height = 0;
|
|
|
+ for (let i = 0; i < config.length; i++) {
|
|
|
+ this.context.drawImage(images[config[i].i - 1], config[i].x, config[i].y, config[i].w, config[i].h, 0, height, config[i].w * scale * this.ratio, config[i].h * scale * this.ratio);
|
|
|
+ height += (config[i].h * scale * this.ratio);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.reset()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.drawText = function(text, left, top) {
|
|
|
+ this.context.font = this.fontConfig.size * this.ratio + 'px ' + this.fontConfig.name
|
|
|
+ this.context.fillStyle = this.fontConfig.color
|
|
|
+ this.context.fillText(text, left * this.ratio, top * this.ratio)
|
|
|
+}
|
|
|
+
|
|
|
+EasyCanvas.prototype.drawShape = function(trace) {
|
|
|
+ if (trace && trace.length > 0) {
|
|
|
+ this.context.lineWidth = this.lineConfig.width
|
|
|
+ this.context.strokeStyle = this.lineConfig.color
|
|
|
+
|
|
|
+ this.context.beginPath()
|
|
|
+ this.context.moveTo(trace[0].x, trace[0].y)
|
|
|
+ for (let i = 1; i < trace.length; i++) {
|
|
|
+ this.context.lineTo(trace[i].x, trace[i].y)
|
|
|
+ this.context.stroke()
|
|
|
+ }
|
|
|
+ this.context.closePath()
|
|
|
+ }
|
|
|
+}
|