chore: 字体文件
This commit is contained in:
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'PoppinsBold';
|
font-family: 'PoppinsBold';
|
||||||
src: url('./Poppins-SemiBold.ttf') format('truetype');
|
src: url('./Poppins-SemiBold.ttf') format('ttf');
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|||||||
BIN
src/assets/css/fonts/ARIAL.ttf
Normal file
BIN
src/assets/css/fonts/ARIAL.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/ARIALBD.ttf
Normal file
BIN
src/assets/css/fonts/ARIALBD.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/ArialMdm.ttf
Normal file
BIN
src/assets/css/fonts/ArialMdm.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/InstrumentSans-Bold.ttf
Normal file
BIN
src/assets/css/fonts/InstrumentSans-Bold.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/InstrumentSans-Regular.ttf
Normal file
BIN
src/assets/css/fonts/InstrumentSans-Regular.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/Poppins-Medium.ttf
Normal file
BIN
src/assets/css/fonts/Poppins-Medium.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/Poppins-Regular.ttf
Normal file
BIN
src/assets/css/fonts/Poppins-Regular.ttf
Normal file
Binary file not shown.
BIN
src/assets/css/fonts/Poppins-SemiBold.ttf
Normal file
BIN
src/assets/css/fonts/Poppins-SemiBold.ttf
Normal file
Binary file not shown.
41
src/assets/css/fonts/fontFamily.css
Normal file
41
src/assets/css/fonts/fontFamily.css
Normal file
@@ -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');
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import 'normalize.css'
|
|||||||
import './assets/css/style.css'
|
import './assets/css/style.css'
|
||||||
import SvgIcon from "@/components/SvgIcon/index.vue";
|
import SvgIcon from "@/components/SvgIcon/index.vue";
|
||||||
import "virtual:svg-icons-register";
|
import "virtual:svg-icons-register";
|
||||||
|
// import './assets/css/fonts/fontFamily.css'
|
||||||
|
|
||||||
import i18n from "./lang/index";
|
import i18n from "./lang/index";
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,87 @@
|
|||||||
import { getUniversalZoomLevel } from '@/utils/tools'
|
import { getUniversalZoomLevel } from '@/utils/util'
|
||||||
|
import MyEvent from '@/utils/myEvent'
|
||||||
|
|
||||||
const maxWidth = 1920;
|
let flexible = (designWidth, maxWidth, minWidth) => {
|
||||||
const minWidth = 500;
|
var doc = document,
|
||||||
let flexible = (designWidth = 1920) => {
|
win = window,
|
||||||
var doc = document, win = window, docEl = doc.documentElement, remStyle = document.createElement("style"), tid;
|
docEl = doc.documentElement,
|
||||||
|
remStyle = document.createElement('style'),
|
||||||
|
tid
|
||||||
|
designWidth = designWidth || 1920
|
||||||
|
maxWidth = maxWidth || 2560
|
||||||
|
minWidth = minWidth || 1024
|
||||||
|
let oldDesignWidth = designWidth
|
||||||
|
|
||||||
|
// minWidth = minWidth || 1024;
|
||||||
function refreshRem() {
|
function refreshRem() {
|
||||||
var width = docEl.getBoundingClientRect().width;
|
var width = docEl.getBoundingClientRect().width
|
||||||
var height = docEl.getBoundingClientRect().height;
|
var height = docEl.getBoundingClientRect().height
|
||||||
width = getUniversalZoomLevel() * width
|
width = getUniversalZoomLevel() * width
|
||||||
height = getUniversalZoomLevel() * height
|
height = getUniversalZoomLevel() * height
|
||||||
if (width > maxWidth) width = maxWidth;
|
maxWidth = maxWidth || 1920
|
||||||
if (width < minWidth) width = minWidth;
|
if (width < 1100) {
|
||||||
var rem = (width * 10 / designWidth).toFixed(2);
|
document.body.classList.add('ipad')
|
||||||
docEl.style.fontSize = rem + 'px'
|
} else {
|
||||||
|
document.body.classList.remove('ipad')
|
||||||
}
|
}
|
||||||
//要等 wiewport 设置好后才能执行 refreshRem,不然 refreshRem 会执行2次;
|
if (width > 768) {
|
||||||
refreshRem();
|
if (width / height > 1.98) width = height * 1.98
|
||||||
win.addEventListener("resize", function () {
|
width > maxWidth && (width = maxWidth)
|
||||||
clearTimeout(tid); //防止执行两次
|
width < minWidth && (width = minWidth)
|
||||||
tid = setTimeout(refreshRem, 200);
|
designWidth = oldDesignWidth
|
||||||
}, false);
|
} else {
|
||||||
|
designWidth = 393
|
||||||
|
}
|
||||||
|
console.log(width, designWidth)
|
||||||
|
|
||||||
win.addEventListener("pageshow", function (e) {
|
// var rem = width * 10 / designWidth;
|
||||||
if (e.persisted) { // 浏览器后退的时候重新计算
|
var rem = Math.round((width * 10) / designWidth)
|
||||||
clearTimeout(tid);
|
docEl.style.fontSize = rem + 'px'
|
||||||
tid = setTimeout(refreshRem, 200);
|
remStyle.innerHTML = 'html{font-size:' + rem + 'px;}'
|
||||||
|
MyEvent.emit('remChange', rem)
|
||||||
}
|
}
|
||||||
}, false);
|
// 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
|
export default flexible
|
||||||
703
src/utils/util.ts
Normal file
703
src/utils/util.ts
Normal file
@@ -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
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user