diff --git a/public/css/fonts/fontFamily.css b/public/css/fonts/fontFamily.css index e7d97b6..d68bea0 100644 --- a/public/css/fonts/fontFamily.css +++ b/public/css/fonts/fontFamily.css @@ -27,7 +27,7 @@ @font-face { font-family: 'PoppinsBold'; - src: url('./Poppins-SemiBold.ttf') format('truetype'); + src: url('./Poppins-SemiBold.ttf') format('ttf'); } @font-face { diff --git a/src/assets/css/fonts/ARIAL.ttf b/src/assets/css/fonts/ARIAL.ttf new file mode 100644 index 0000000..8682d94 Binary files /dev/null and b/src/assets/css/fonts/ARIAL.ttf differ diff --git a/src/assets/css/fonts/ARIALBD.ttf b/src/assets/css/fonts/ARIALBD.ttf new file mode 100644 index 0000000..a6037e6 Binary files /dev/null and b/src/assets/css/fonts/ARIALBD.ttf differ diff --git a/src/assets/css/fonts/ArialMdm.ttf b/src/assets/css/fonts/ArialMdm.ttf new file mode 100644 index 0000000..3222b81 Binary files /dev/null and b/src/assets/css/fonts/ArialMdm.ttf differ diff --git a/src/assets/css/fonts/InstrumentSans-Bold.ttf b/src/assets/css/fonts/InstrumentSans-Bold.ttf new file mode 100644 index 0000000..f602dce Binary files /dev/null and b/src/assets/css/fonts/InstrumentSans-Bold.ttf differ diff --git a/src/assets/css/fonts/InstrumentSans-Regular.ttf b/src/assets/css/fonts/InstrumentSans-Regular.ttf new file mode 100644 index 0000000..14c6113 Binary files /dev/null and b/src/assets/css/fonts/InstrumentSans-Regular.ttf differ diff --git a/src/assets/css/fonts/Poppins-Medium.ttf b/src/assets/css/fonts/Poppins-Medium.ttf new file mode 100644 index 0000000..6bcdcc2 Binary files /dev/null and b/src/assets/css/fonts/Poppins-Medium.ttf differ diff --git a/src/assets/css/fonts/Poppins-Regular.ttf b/src/assets/css/fonts/Poppins-Regular.ttf new file mode 100644 index 0000000..9f0c71b Binary files /dev/null and b/src/assets/css/fonts/Poppins-Regular.ttf differ diff --git a/src/assets/css/fonts/Poppins-SemiBold.ttf b/src/assets/css/fonts/Poppins-SemiBold.ttf new file mode 100644 index 0000000..74c726e Binary files /dev/null and b/src/assets/css/fonts/Poppins-SemiBold.ttf differ diff --git a/src/assets/css/fonts/fontFamily.css b/src/assets/css/fonts/fontFamily.css new file mode 100644 index 0000000..e7d97b6 --- /dev/null +++ b/src/assets/css/fonts/fontFamily.css @@ -0,0 +1,41 @@ +/* 字体定义 */ +@font-face { + font-family: 'Arial'; + src: url('./ARIAL.ttf') format('truetype'); +} + +@font-face { + font-family: 'ArialBold'; + src: url('./ARIALBD.ttf') format('truetype'); +} + +@font-face { + font-family: 'ArialMedium'; + src: url('./ArialMdm.ttf') format('truetype'); +} + +@font-face { + font-family: 'Poppins'; + src: url('./Poppins-Regular.ttf') format('truetype'); + font-weight: normal; +} + +@font-face { + font-family: 'PoppinsMedium'; + src: url('./Poppins-Medium.ttf') format('truetype'); +} + +@font-face { + font-family: 'PoppinsBold'; + src: url('./Poppins-SemiBold.ttf') format('truetype'); +} + +@font-face { + font-family: 'Instrument'; + src: url('./InstrumentSans-Regular.ttf') format('truetype'); +} + +@font-face { + font-family: 'InstrumentBold'; + src: url('./InstrumentSans-Bold.ttf') format('truetype'); +} diff --git a/src/main.ts b/src/main.ts index d248983..bee4635 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,7 @@ import 'normalize.css' import './assets/css/style.css' import SvgIcon from "@/components/SvgIcon/index.vue"; import "virtual:svg-icons-register"; +// import './assets/css/fonts/fontFamily.css' import i18n from "./lang/index"; diff --git a/src/utils/flexible.js b/src/utils/flexible.js index ef32bb2..aeb5333 100644 --- a/src/utils/flexible.js +++ b/src/utils/flexible.js @@ -1,32 +1,87 @@ -import { getUniversalZoomLevel } from '@/utils/tools' +import { getUniversalZoomLevel } from '@/utils/util' +import MyEvent from '@/utils/myEvent' -const maxWidth = 1920; -const minWidth = 500; -let flexible = (designWidth = 1920) => { - var doc = document, win = window, docEl = doc.documentElement, remStyle = document.createElement("style"), tid; - function refreshRem() { - var width = docEl.getBoundingClientRect().width; - var height = docEl.getBoundingClientRect().height; - width = getUniversalZoomLevel() * width - height = getUniversalZoomLevel() * height - if (width > maxWidth) width = maxWidth; - if (width < minWidth) width = minWidth; - var rem = (width * 10 / designWidth).toFixed(2); - docEl.style.fontSize = rem + 'px' - } - //要等 wiewport 设置好后才能执行 refreshRem,不然 refreshRem 会执行2次; - refreshRem(); - win.addEventListener("resize", function () { - clearTimeout(tid); //防止执行两次 - tid = setTimeout(refreshRem, 200); - }, false); +let flexible = (designWidth, maxWidth, minWidth) => { + var doc = document, + win = window, + docEl = doc.documentElement, + remStyle = document.createElement('style'), + tid + designWidth = designWidth || 1920 + maxWidth = maxWidth || 2560 + minWidth = minWidth || 1024 + let oldDesignWidth = designWidth - win.addEventListener("pageshow", function (e) { - if (e.persisted) { // 浏览器后退的时候重新计算 - clearTimeout(tid); - tid = setTimeout(refreshRem, 200); - } - }, false); -}; + // minWidth = minWidth || 1024; + function refreshRem() { + var width = docEl.getBoundingClientRect().width + var height = docEl.getBoundingClientRect().height + width = getUniversalZoomLevel() * width + height = getUniversalZoomLevel() * height + maxWidth = maxWidth || 1920 + if (width < 1100) { + document.body.classList.add('ipad') + } else { + document.body.classList.remove('ipad') + } + if (width > 768) { + if (width / height > 1.98) width = height * 1.98 + width > maxWidth && (width = maxWidth) + width < minWidth && (width = minWidth) + designWidth = oldDesignWidth + } else { + designWidth = 393 + } + console.log(width, designWidth) -export default flexible \ No newline at end of file + // var rem = width * 10 / designWidth; + var rem = Math.round((width * 10) / designWidth) + docEl.style.fontSize = rem + 'px' + remStyle.innerHTML = 'html{font-size:' + rem + 'px;}' + MyEvent.emit('remChange', rem) + } + // if (docEl.firstElementChild) { + // docEl.firstElementChild.appendChild(remStyle); + // } else { + // var wrap = doc.createElement("div"); + // wrap.appendChild(remStyle); + // doc.write(wrap.innerHTML); + // wrap = null; + // } + //要等 wiewport 设置好后才能执行 refreshRem,不然 refreshRem 会执行2次; + refreshRem() + win.addEventListener( + 'resize', + function () { + clearTimeout(tid) //防止执行两次 + tid = setTimeout(refreshRem, 300) + }, + false + ) + + win.addEventListener( + 'pageshow', + function (e) { + if (e.persisted) { + // 浏览器后退的时候重新计算 + clearTimeout(tid) + tid = setTimeout(refreshRem, 300) + } + }, + false + ) + + if (doc.readyState === 'complete') { + doc.body.style.fontSize = '16px' + } else { + doc.addEventListener( + 'DOMContentLoaded', + function (e) { + doc.body.style.fontSize = '16px' + }, + false + ) + } +} + +export default flexible diff --git a/src/utils/util.ts b/src/utils/util.ts new file mode 100644 index 0000000..fae7b0c --- /dev/null +++ b/src/utils/util.ts @@ -0,0 +1,703 @@ +const isEmail = (email) => { + // let reg = /^([A-Za-z0-9_\-\.])+\@([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,})$/ + let reg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/ + let result = reg.test(email) + return result +} +function getUniversalZoomLevel() { + // 现代浏览器方案 + if (window.visualViewport) { + return window.visualViewport.scale + } + // 备用方案1 + if (window.devicePixelRatio) { + return window.devicePixelRatio + } + // 备用方案2(不精确) + return window.outerWidth / window.innerWidth +} +const getUploadUrl = () => { + let url = import.meta.env.VITE_APP_BASE_URL || '' + // let url = "http://18.167.251.121:10086" + return url +} +const getMinioUrl = (url) => { + if (!isValidMinioUrl(url)) return '' + if (isUrl(url)) { + const { pathname } = new URL(url) + const result = pathname.slice(1) + return result + } + return '' +} +function isValidMinioUrl(url) { + return url.includes('www.minio-api.aida.com.hk') // 关键特征检测 +} +function isUrl(str) { + try { + new URL(str) + return true + } catch (e) { + return false + } +} +const rgbaToHex = (rgba) => { + // rgba转16进制 + let hex = '#' + rgba.forEach((i, index) => { + if (index == 3) { + hex += Math.round(i * 255).toString(16) + } else { + hex += Number(i).toString(16).padStart(2, '0') + } + }) + + return hex +} + +function base64ToFile(urlData, name) { + let arr = urlData.split(',') + let mime = arr[0].match(/:(.*?);/)[1] + let bstr = atob(arr[1]) + let n = bstr.length + let u8arr = new Uint8Array(n) + while (n--) { + u8arr[n] = bstr.charCodeAt(n) + } + return new File([u8arr], name, { + type: mime + }) +} + +function dataURLtoBlob(dataurl) { + //吧data url转为blob对象 + var arr = dataurl.split(',') + var mime = arr[0].match(/:(.*?);/)[1] + var bstr = atob(arr[1]) + var n = bstr.length + var u8arr = new Uint8Array(n) + while (n--) { + u8arr[n] = bstr.charCodeAt(n) + } + return new Blob([u8arr], { type: mime }) +} + +function blobToFile(blob, fileName) { + //给blob文件设置名字和日期 + blob.lastModifiedDate = new Date() + blob.name = fileName + return blob +} +//下载图片 +function downloadIamge(imgsrc, name) { + // 下载图片地址和图片名 + var image = new Image() + // 解决跨域 Canvas 污染问题 + image.setAttribute('crossOrigin', 'anonymous') + image.onload = function () { + var canvas = document.createElement('canvas') + canvas.width = image.width + canvas.height = image.height + var context = canvas.getContext('2d') + context.drawImage(image, 0, 0, image.width, image.height) + var url = canvas.toDataURL('image/png') // 得到图片的base64编码数据 + var a = document.createElement('a') // 生成一个a元素 + var event = new MouseEvent('click') // 创建一个单击事件 + a.download = name || 'generate' // 设置图片名称 + a.href = url // 将生成的URL设置为a.href属性 + a.target = '_blank' + a.dispatchEvent(event) // 触发a的单击事件 + image.remove() + } + image.src = imgsrc +} +async function downloadVideoWithFetch(url, filename) { + try { + const response = await fetch(url) + const blob = await response.blob() + const blobUrl = URL.createObjectURL(blob) + + const a = document.createElement('a') + a.href = blobUrl + a.download = filename || 'video.mp4' + document.body.appendChild(a) + a.click() + + // 清理 + setTimeout(() => { + document.body.removeChild(a) + URL.revokeObjectURL(blobUrl) + }, 100) + } catch (error) { + console.error('下载失败:', error) + } +} +function dataURLtoFile(dataurl, filename) { + //吧url转为文件对象,指定文件名称 + var arr = dataurl.split(','), + mime = arr[0].match(/:(.*?);/)[1], + bstr = atob(arr[1]), + n = bstr.length, + u8arr = new Uint8Array(n) + while (n--) { + u8arr[n] = bstr.charCodeAt(n) + } + var blob = dataURLtoBlob(dataurl) + return blobToFile(blob, filename) +} + +const base64toFile = (dataurl, filename = 'file') => { + //转换base64 + let arr = dataurl.split(',') + let mime = arr[0].match(/:(.*?);/)[1] + let suffix = mime.split('/')[1] + let bstr = atob(arr[1]) + let n = bstr.length + let u8arr = new Uint8Array(n) + while (n--) { + u8arr[n] = bstr.charCodeAt(n) + } + return new File([u8arr], `${filename}.${suffix}`, { + type: mime + }) +} + +const UrlToFile = async (url, imageName) => { + const response = await fetch(url) + const blob = await response.blob() + return new File([blob], imageName, { type: 'image/png' }) +} + +function rgbToHsv([R, G, B]) { + //根据rgb获取hsv + R /= 255 + G /= 255 + B /= 255 + const max = Math.max(R, G, B) + const min = Math.min(R, G, B) + const delta = max - min + var H, S, V + if (delta === 0) { + H = 0 + } else if (max === R) { + H = ((G - B) / delta) % 6 + } else if (max === G) { + H = (B - R) / delta + 2 + } else { + // max === B + H = (R - G) / delta + 4 + } + H = Math.round(H * 60) // 范围为 0-360 + if (H < 0) { + H = 360 + H + } + if (max === 0) { + S = 0 + } else { + S = delta / max + } + S = Math.round(S * 100) // 范围为 0-100 + V = Math.round(max * 100) // 范围为 0-100 + return [H, S, V] +} + +const formatTime = (timestamp, fmt) => { + //吧时间戳转为YYYY-MM-DD hh:mm:ss格式 + // date = new Date(), fmt = 'MM/dd/yyyy'; + let date = new Date() + date.setTime(timestamp * 1000) + + if (!fmt) { + formatRule ? (fmt = formatRule) : (fmt = 'YYYY-MM-DD hh:mm:ss') + } + // console.log(formatRule) + let o = { + 'M+': date.getMonth() + 1, // 月份 + 'D+': date.getDate(), // 日 + 'h+': date.getHours(), // 小时 + 'm+': date.getMinutes(), // 分 + 's+': date.getSeconds(), // 秒 + 'q+': Math.floor((date.getMonth() + 3) / 3), // 季度 + 'S+': date.getMilliseconds(), // 毫秒 + a: date.getHours() > 12 ? 'PM' : 'AM' // 上午还是下午 + } + if (/(Y+)/.test(fmt)) { + fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) + } + if (/(a)/.test(fmt) && o['h+'] > 12) { + o['h+'] = o['h+'] - 12 + } + for (let k in o) { + if (new RegExp('(' + k + ')').test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length) + ) + } + } + + return fmt +} + +const isMoible = () => { + //判断是否是移动端 + let is_mobile = + navigator.userAgent + .toLowerCase() + .match( + /(ipad|ipod|iphone|android|coolpad|mmp|smartphone|midp|wap|xoom|symbian|j2me|blackberry|wince)/i + ) != null + // alert(navigator.userAgent.toLowerCase()) + var isiPad = navigator.maxTouchPoints && navigator.maxTouchPoints > 1 + // if (is_mobile) { + // return true//判断是否在正则内 + // } else if(window.matchMedia("(pointer:fine)").matches){ + // return false//判断是否支持鼠标 + // }else{ + // isiPad//判断触摸点 + // } + if (is_mobile) { + return true //判断是否在正则内 + } else { + return isiPad //判断触摸点 + } +} + +let setPubDate = (date, t) => { + const timestamp = new Date(date) + const now = new Date() + // 计算时间差(以毫秒为单位) + const differenceMs = now - timestamp + const seconds = Math.floor(differenceMs / 1000) + const minutes = Math.floor(seconds / 60) + const hours = Math.floor(minutes / 60) + const days = Math.floor(hours / 24) + const weeks = Math.floor(days / 7) + const months = Math.floor(days / 30) + const years = Math.floor(days / 365) + + // 根据时间差的大小返回不同的描述 + if (years > 0) { + return `${years} ${t('newScaleImage.yearsAgo')}` + } else if (months > 0) { + return `${months} ${t('newScaleImage.monthsAgo')}` + } else if (weeks > 0) { + return `${weeks} ${t('newScaleImage.WeeksAgo')}` + } else if (days > 0) { + return `${t('newScaleImage.daysAgo')}` + } else if (hours > 0) { + return `${hours} ${t('newScaleImage.HoursAgo')}` + } else if (minutes > 0) { + return `${minutes} ${t('newScaleImage.minutesAgo')}` + } else { + return `${t('newScaleImage.minuteAgo')}` + } +} + +function getBrowserInfo() { + //获取是什么浏览器 + var agent = navigator.userAgent.toLowerCase() + var userAgent = navigator.userAgent + var regStr_ie = /msie [\d.]+;/gi + var regStr_ff = /firefox\/[\d.]+/gi + var regStr_chrome = /chrome\/[\d.]+/gi + var regStr_saf = /safari\/[\d.]+/gi + var isIE = userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1 //判断是否IE<11浏览器 + var isEdge = userAgent.indexOf('Edge') > -1 && !isIE //判断是否IE的Edge浏览器 + //IE + if (agent.indexOf('msie') > 0) { + return agent.match(regStr_ie) + } + //firefox + if (agent.indexOf('firefox') > 0) { + return agent.match(regStr_ff) + } + //Chrome + if (agent.indexOf('chrome') > 0) { + return agent.match(regStr_chrome) + } + //Safari + if (agent.indexOf('safari') > 0 && agent.indexOf('chrome') < 0) { + return agent.match(regStr_saf) + } +} +async function murmur() { + //生成唯一标识 ,暂时没有使用 + return await new Promise((resolve, reject) => { + Fingerprint2.get(function (components) { + const values = components.map(function (component, index) { + if (index === 0) { + //把微信浏览器里UA的wifi或4G等网络替换成空,不然切换网络会ID不一样 + return component.value.replace(/\bNetType\/\w+\b/, '') + } + return component.value + }) + // 生成最终id murmur + let murmur = Fingerprint2.x64hash128(values.join(''), 31) + resolve(murmur) + }) + }) +} +/** + * @description: 计算canvas渐变起始坐标 + * @param {number} canvas width + * @param {number} canvas height + * @param {number} angle 角度 + * @return {*} + */ +function calculateGradientCoordinate(width, height, angle) { + if (angle >= 360) angle = angle - 360 + if (angle < 0) angle = angle + 360 + angle = Math.round(angle) + + // 当渐变轴垂直于矩形水平边上的两种结果 + if (angle === 0) { + return { + x0: Math.round(width / 2), + y0: height, + x1: Math.round(width / 2), + y1: 0 + } + } + if (angle === 180) { + return { + x0: Math.round(width / 2), + y0: 0, + x1: Math.round(width / 2), + y1: height + } + } + + // 当渐变轴垂直于矩形垂直边上的两种结果 + if (angle === 90) { + return { + x0: 0, + y0: Math.round(height / 2), + x1: width, + y1: Math.round(height / 2) + } + } + if (angle === 270) { + return { + x0: width, + y0: Math.round(height / 2), + x1: 0, + y1: Math.round(height / 2) + } + } + + // 从矩形左下角至右上角的对角线的角度 + const alpha = Math.round( + (Math.asin(width / Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))) * 180) / Math.PI + ) + + // 当渐变轴分别于矩形的两条对角线重合情况下的四种结果 + if (angle === alpha) { + return { + x0: 0, + y0: height, + x1: width, + y1: 0 + } + } + if (angle === 180 - alpha) { + return { + x0: 0, + y0: 0, + x1: width, + y1: height + } + } + if (angle === 180 + alpha) { + return { + x0: width, + y0: 0, + x1: 0, + y1: height + } + } + if (angle === 360 - alpha) { + return { + x0: width, + y0: height, + x1: 0, + y1: 0 + } + } + + // 以矩形的中点为坐标原点,向上为Y轴正方向,向右为X轴正方向建立直角坐标系 + let x0 = 0, + y0 = 0, + x1 = 0, + y1 = 0 + + // 当渐变轴与矩形的交点落在水平线上 + if ( + angle < alpha || // 处于第一象限 + (angle > 180 - alpha && angle < 180) || // 处于第二象限 + (angle > 180 && angle < 180 + alpha) || // 处于第三象限 + angle > 360 - alpha // 处于第四象限 + ) { + // 将角度乘以(PI/180)即可转换为弧度 + const radian = (angle * Math.PI) / 180 + // 当在第一或第四象限,y是height / 2,否则y是-height / 2 + const y = angle < alpha || angle > 360 - alpha ? height / 2 : -height / 2 + const x = Math.tan(radian) * y + // 当在第一或第二象限,l是width / 2 - x,否则l是-width / 2 - x + const l = angle < alpha || (angle > 180 - alpha && angle < 180) ? width / 2 - x : -width / 2 - x + const n = Math.pow(Math.sin(radian), 2) * l + x1 = x + n + y1 = y + n / Math.tan(radian) + x0 = -x1 + y0 = -y1 + } + + // 当渐变轴与矩形的交点落在垂直线上 + if ( + (angle > alpha && angle < 90) || // 处于第一象限 + (angle > 90 && angle < 180 - alpha) || // 处于第二象限 + (angle > 180 + alpha && angle < 270) || // 处于第三象限 + (angle > 270 && angle < 360 - alpha) // 处于第四象限 + ) { + // 将角度乘以(PI/180)即可转换为弧度 + const radian = ((90 - angle) * Math.PI) / 180 + // 当在第一或第二象限,x是width / 2,否则x是-width / 2 + const x = + (angle > alpha && angle < 90) || (angle > 90 && angle < 180 - alpha) ? width / 2 : -width / 2 + const y = Math.tan(radian) * x + // 当在第一或第四象限,l是height / 2 - y,否则l是-height / 2 - y + const l = + (angle > alpha && angle < 90) || (angle > 270 && angle < 360 - alpha) + ? height / 2 - y + : -height / 2 - y + const n = Math.pow(Math.sin(radian), 2) * l + x1 = x + n / Math.tan(radian) + y1 = y + n + x0 = -x1 + y0 = -y1 + } + + // 坐标系更改为canvas标准,Y轴向下为正方向 + x0 = Math.round(x0 + width / 2) + y0 = Math.round(height / 2 - y0) + x1 = Math.round(x1 + width / 2) + y1 = Math.round(height / 2 - y1) + return { x0, y0, x1, y1 } +} + +const setGradual = (colorObj, colorWidth, colorHeight) => { + return new Promise((resolve, reject) => { + let width = colorWidth || 320 + let height = colorHeight || 700 + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + canvas.width = width + canvas.height = height + let { x0, y0, x1, y1 } = calculateGradientCoordinate(width, height, colorObj.angle) + const gradient = ctx.createLinearGradient(x0, y0, x1, y1) + colorObj.gradientList.forEach((item) => { + let left = item.left.split('%')[0] / 100 + let rgba = `rgba(${item.rgba.r},${item.rgba.g},${item.rgba.b},${item.rgba.a})` + gradient.addColorStop(left, rgba) // 起始颜色 + }) + ctx.fillStyle = gradient + ctx.fillRect(0, 0, width, height) + // let dataURL = canvas.toDataURL('image/jpg'); + resolve(canvas.toDataURL('image/jpg')) + }) +} +function segmentImage(markerImage, fullImage, size) { + return new Promise((resolve, reject) => { + const markerCanvas = document.createElement('canvas') + const fullCanvas = document.createElement('canvas') + const nullCanvas = document.createElement('canvas') + const ctx1 = markerCanvas.getContext('2d') + const ctx2 = fullCanvas.getContext('2d') + const ctx3 = nullCanvas.getContext('2d') + markerCanvas.width = size.width + markerCanvas.height = size.height + fullCanvas.height = size.height + fullCanvas.width = size.width + nullCanvas.height = size.height + nullCanvas.width = size.width + let targetFrontUrl = '' + let targetBackUrl = '' + const marker = new Image() + const full = new Image() + marker.crossOrigin = 'anonymous' + full.crossOrigin = 'anonymous' + marker.onload = () => { + ctx1.drawImage(marker, 0, 0, size.width, size.height) + full.onload = () => { + ctx2.drawImage(full, 0, 0, size.width, size.height) + segmentImageItem() + } + full.src = fullImage + } + marker.src = markerImage + function segmentImageItem() { + const markerData = ctx1.getImageData(0, 0, size.width, size.height) + const fullData = ctx2.getImageData(0, 0, size.width, size.height) + + const color1 = { r: 255, g: 0, b: 0 } // 第一个颜色 + const color2 = { r: 0, g: 255, b: 0 } // 第二个颜色 + + const threshold = 100 // 颜色匹配的容差 + // const isColorMatch = (r, g, b, color) => + // (Math.abs(r - color.r) < threshold) || (Math.abs(0 - color.r) < threshold) && + // (Math.abs(g - color.g) < threshold) || (Math.abs(0 - color.g) < threshold) && + // (Math.abs(b - color.b) < threshold) || (Math.abs(0 - color.b) < threshold) + + const isColorMatch = (r, g, b, color) => + (color.r >= color.g && r >= g) || (color.r < color.g && r < g) + + // (Math.abs(b - color.b) < threshold || Math.abs(0 - color.b) < threshold) + + const output1 = ctx3.createImageData(size.width, size.height) + const output2 = ctx3.createImageData(size.width, size.height) + for (let i = 0; i < markerData.data.length; i += 4) { + const r = markerData.data[i] + const g = markerData.data[i + 1] + const b = markerData.data[i + 2] + let a = markerData.data[i + 3] + a > 1 ? (a = 255) : 0 + if (r >= g && a > 1) { + // 将完整图像中对应的像素复制到第一个输出图像 + output1.data[i] = fullData.data[i] + output1.data[i + 1] = fullData.data[i + 1] + output1.data[i + 2] = fullData.data[i + 2] + output1.data[i + 3] = fullData.data[i + 3] + // output1.data[i] = 158; + // output1.data[i + 1] = 51; + // output1.data[i + 2] = 0; + // output1.data[i + 3] = 255; + // 第二个图像的像素置为透明 + output2.data[i] = 0 + output2.data[i + 1] = 0 + output2.data[i + 2] = 0 + output2.data[i + 3] = 0 + } else if (r < g && a > 1) { + // 将完整图像中对应的像素复制到第二个输出图像 + output2.data[i] = fullData.data[i] + output2.data[i + 1] = fullData.data[i + 1] + output2.data[i + 2] = fullData.data[i + 2] + output2.data[i + 3] = fullData.data[i + 3] + // output2.data[i] = 158; + // output2.data[i + 1] = 51; + // output2.data[i + 2] = 0; + // output2.data[i + 3] = 255; + // 第一个图像的像素置为透明 + output1.data[i] = 0 + output1.data[i + 1] = 0 + output1.data[i + 2] = 0 + output1.data[i + 3] = 0 + } else { + // 两个图像的像素都置为透明 + output1.data[i] = 0 + output1.data[i + 1] = 0 + output1.data[i + 2] = 0 + output1.data[i + 3] = 0 + + output2.data[i] = 0 + output2.data[i + 1] = 0 + output2.data[i + 2] = 0 + output2.data[i + 3] = 0 + } + } + const createImageURL = (imageData) => { + const canvas = document.createElement('canvas') + canvas.width = size.width + canvas.height = size.height + const ctx = canvas.getContext('2d') + ctx.putImageData(imageData, 0, 0) + let data = canvas.toDataURL('image/png') + canvas.remove() + return data + } + targetBackUrl = createImageURL(output2) + targetFrontUrl = createImageURL(output1) + resolve({ targetFrontUrl, targetBackUrl }) + markerCanvas.remove() + fullCanvas.remove() + nullCanvas.remove() + } + }) +} + +/** + * 处理PNG图片:透明度转白色,其他颜色转透明 + * @param {string} sketchImage - 原始图片 + * @returns {Promise} 处理后的ase64 + */ +function sketchToMask(sketchImage) { + return new Promise((resolve, reject) => { + const img = new Image() + img.crossOrigin = 'anonymous' + img.onload = function () { + try { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + canvas.width = img.width + canvas.height = img.height + ctx.drawImage(img, 0, 0) + const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height) + const data = imageData.data + + for (let i = 0; i < data.length; i += 4) { + const r = data[i] + const g = data[i + 1] + const b = data[i + 2] + const a = data[i + 3] + if (a > 0) { + data[i] = 0 + data[i + 1] = 0 + data[i + 2] = 0 + data[i + 3] = 0 + } else { + // 完全透明的像素 -> 纯白色 + data[i] = 255 + data[i + 1] = 255 + data[i + 2] = 255 + data[i + 3] = 255 + } + } + ctx.putImageData(imageData, 0, 0) + const base64 = canvas.toDataURL('image/png') + resolve(base64) + } catch (error) { + reject(error) + } + } + + img.onerror = function () { + reject(new Error('图片加载失败')) + } + + img.src = sketchImage + }) +} +export { + isEmail, + getUploadUrl, + getUniversalZoomLevel, + rgbaToHex, + getMinioUrl, + base64ToFile, + dataURLtoFile, + blobToFile, + base64toFile, + rgbToHsv, + formatTime, + dataURLtoBlob, + isMoible, + downloadIamge, + downloadVideoWithFetch, + getBrowserInfo, + setPubDate, + murmur, + setGradual, + calculateGradientCoordinate, + segmentImage, + UrlToFile, + sketchToMask +}