初始化仓库

This commit is contained in:
wangxiaowei
2026-04-14 16:54:04 +08:00
commit 967c25b397
553 changed files with 106514 additions and 0 deletions

View File

@ -0,0 +1,141 @@
import {base64ToPath} from '../l-painter/utils.js'
const styles = (v = '') => v.split(';').filter(v => v && !/^[\n\s]+$/.test(v)).map(v => {
const item = v.split(':');
return {
[item[0]
.replace(/-([a-z])/g, function() {
return arguments[1].toUpperCase()
})
.replace(/\s+/g, '')
]: item?. [1]?.replace(/^\s+/, '')?.replace(/\s+$/, '') || ''
}
})
export function parent(parent) {
return {
provide() {
return {
[parent]: this
}
},
data() {
return {
el: {
css: {},
views: []
},
}
},
watch: {
css: {
handler(v) {
if (this.canvasId) {
this.el.css = typeof v == 'object' ? v : v && Object.assign(...styles(v)) || {}
this.canvasWidth = this.el.css?.width || this.canvasWidth
this.canvasHeight = this.el.css?.height || this.canvasHeight
}
},
immediate: true
}
}
}
}
export function children(parent, options = {}) {
const indexKey = options.indexKey || 'index'
return {
inject: {
[parent]: {
default: null
}
},
watch: {
el: {
handler(v, o) {
if (JSON.stringify(v) != JSON.stringify(o))
this.bindRelation()
},
deep: true,
immediate: true
},
src: {
handler(v, o) {
if (v != o)
this.bindRelation()
},
immediate: true
},
text: {
handler(v, o) {
if (v != o) this.bindRelation()
},
immediate: true
},
css: {
handler(v, o) {
if (v != o)
this.el.css = typeof v == 'object' ? v : v && Object.assign(...styles(v)) || {}
},
immediate: true
},
replace: {
handler(v, o) {
if (JSON.stringify(v) != JSON.stringify(o))
this.bindRelation()
},
deep: true,
immediate: true
},
},
created() {
Object.defineProperty(this, 'parent', {
get: () => {
return this[parent]
},
})
Object.defineProperty(this, 'index', {
get: () => {
this.bindRelation();
return this.parent?.el.views?.indexOf(this.el)
},
});
this.el.type = this.type
},
beforeDestroy() {
if (this.parent) {
this.parent.el.views = this.parent.el.views.filter(
(item) => item._uid !== this._uid
);
}
},
methods: {
bindRelation() {
if (!this.el._uid) {
this.el._uid = this._uid
}
if (['text', 'qrcode'].includes(this.type)) {
this.el.text = this.$slots?.default?. [0]?.text || this.text?.replace(/\\n/g, '\n')
}
if (this.type == 'text' && this.replace) {
this.el.replace = this.replace
}
if (this.type == 'image') {
this.el.src = this.src
}
// || this.parent.el.views.indexOf(this.el) !== -1
if (!this.parent) {
return;
}
let views = this.parent.el.views || [];
if (views.indexOf(this.el) !== -1) {
this.parent.el.views = views.map(v => v._uid == this._uid ? this.el : v)
} else {
this.parent.el.views = [...views, this.el];
}
}
},
mounted() {
this.bindRelation()
},
}
}

View File

@ -0,0 +1,27 @@
<template>
<view></view>
</template>
<script>
import {parent, children} from '../common/relation';
export default {
name: 'lime-painter-image',
mixins:[children('painter')],
props: {
css: [String, Object],
src: String
},
data() {
return {
type: 'image',
el: {
css: {},
src: null
},
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,27 @@
<template>
<view></view>
</template>
<script>
import {parent, children} from '../common/relation';
export default {
name: 'lime-painter-qrcode',
mixins:[children('painter')],
props: {
css: [String, Object],
text: String
},
data() {
return {
type: 'qrcode',
el: {
css: {},
text: null
},
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,28 @@
<template>
<text style="opacity: 0;"><slot/></text>
</template>
<script>
import {parent, children} from '../common/relation';
export default {
name: 'lime-painter-text',
mixins:[children('painter')],
props: {
css: [String, Object],
text: [String, Number],
replace: Object,
},
data() {
return {
type: 'text',
el: {
css: {},
text: null
},
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,26 @@
<template>
<view><slot/></view>
</template>
<script>
import {parent, children} from '../common/relation';
export default {
name: 'lime-painter-view',
mixins:[children('painter'), parent('painter')],
props: {
css: [String, Object],
},
data() {
return {
type: 'view',
el: {
css: {},
views:[]
},
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,523 @@
<template>
<view v-if="canvasId && size" class="lime-painter" :style="size + customStyle">
<!-- #ifndef APP-NVUE -->
<canvas class="lime-painter__canvas" v-if="use2dCanvas" :id="canvasId" type="2d" :style="size"></canvas>
<canvas class="lime-painter__canvas" v-else :canvas-id="canvasId" :style="size" :id="canvasId" :width="boardWidth * dpr" :height="boardHeight * dpr"></canvas>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<web-view
:style="size"
ref="webview"
class="lime-painter__canvas"
@pagefinish="onPageFinish"
@error="onError"
@onPostMessage="onMessage"
></web-view>
<!-- #endif -->
<slot/>
</view>
</template>
<script>
import { toPx, compareVersion, sleep, base64ToPath, pathToBase64, getImageInfo, isBase64 } from './utils';
import {parent} from '../common/relation'
// #ifndef APP-NVUE
import {Painter} from './painter'
// #endif
// #ifdef APP-NVUE
import painterScript from './nvue'
// #endif
export default {
name: 'lime-painter',
mixins:[parent('painter')],
props: {
board: Object,
pathType: {
type: String,
// default: 'url'
// 'base64'、'url'
},
fileType: {
type: String,
default: 'png'
},
quality: {
type: Number,
default: 1
},
css: [String, Object],
width: [Number, String],
height: [Number, String],
pixelRatio: Number,
customStyle: String,
isCanvasToTempFilePath: Boolean,
sleep: {
type: Number,
default: 1000 / 30
},
beforeDelay: {
type: Number,
default: 100
},
afterDelay: {
type: Number,
default: 100
},
// #ifdef MP-WEIXIN || MP-TOUTIAO||MP-ALIPAY
type: {
type: String,
default: '2d'
},
// #endif
},
data() {
return {
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: true,
// #endif
// #ifndef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
use2dCanvas: false,
// #endif
canvasHeight: 150,
canvasWidth: null,
isDrawIng: false,
isPC: false,
inited: false,
name: 'view',
progress: 0,
// #ifdef APP-NVUE
tempFilePath: [],
// #endif
};
},
computed: {
canvasId() {
return `l-painter${this._uid}`
},
size() {
if(this.boardWidth && this.boardHeight) {
return `width:${this.boardWidth}px; height: ${this.boardHeight}px;`;
}
},
dpr() {
return this.pixelRatio || uni.getSystemInfoSync().pixelRatio;
},
boardWidth() {
const {width = 0, canvasWidth = 0} = this
const {width: boardWidth = 0} = this.board?.css || this.board || {}
return Math.max(toPx(width || boardWidth), toPx(canvasWidth));
},
boardHeight() {
const {height= 0, canvasHeight =0} = this
const {height: boardHeight = 0} = this.board?.css || this.board || {}
return Math.max(toPx(height || boardHeight), toPx(canvasHeight));
}
},
watch: {
canvasWidth(v) {
if(this.el.css && !this.el.css?.width) {
this.el.css.width = v
}
},
size(v) {
// #ifdef MP-WEIXIN
if (this.use2dCanvas) {
this.inited = false;
}
// #endif
// #ifdef MP-ALIPAY
this.inited = false;
// #endif
},
},
// #ifdef MP-WEIXIN || MP-TOUTIAO || MP-ALIPAY
created() {
const { SDKVersion, version, platform, environment } = uni.getSystemInfoSync();
// #ifdef MP-WEIXIN
// ios wx7.0.20 createImage bug
this.isPC = /windows/i.test(platform)
this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '2.9.2') >= 0 && !((/ios/i.test(platform) && /7.0.20/.test(version)) || /wxwork/i.test(environment)) && !this.isPC;
// #endif
// #ifdef MP-TOUTIAO
this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '1.78.0') >= 0;
// #endif
// #ifdef MP-TOUTIAO
this.use2dCanvas = this.type === '2d' && compareVersion(SDKVersion, '2.8.0') >= 0;
// #endif
},
// #endif
mounted() {
// #ifdef APP-NVUE
this.webViewInit()
// #endif
this.$nextTick(() => {
setTimeout(() => {
if(this.board) {
this.$watch('board', this.watchRender, {deep: true,immediate: true});
} else if(this.el.views.length) {
this.$watch('el', this.watchRender, {deep: true,immediate: true});
}
},30)
})
},
methods: {
async watchRender(val) {
this.progress = 0
if (JSON.stringify(val) === '{}' || !val) return;
clearTimeout(this.rendertimer)
this.rendertimer = setTimeout(() => {
this.render(val);
}, this.beforeDelay)
},
async setFilePath(path, isEmit) {
let filePath = path
const {pathType} = this
if(pathType == 'base64' && !isBase64(path)) {
filePath = await pathToBase64(path)
} else if(pathType == 'url' && isBase64(path)) {
filePath = await base64ToPath(path)
}
if(isEmit) {
this.$emit('success', filePath);
}
return filePath
},
// #ifdef APP-NVUE
onError(e) {
console.log('onError', e)
},
// onPagestart() {
// console.log('onPagestart')
// },
// onPageFinish() {
// this.$refs.webview.evalJS(`init()`)
// },
// onReceivedTitle() {
// console.log('onReceivedTitle')
// },
onMessage(e) {
const res = e?.detail?.data[0] || null;
if (res?.event) {
if(res.event == 'inited') {
this.inited = true
}
if(res.event == 'layoutChange') {
const data = JSON.parse(res.data)
this.canvasWidth = data.width;
this.canvasHeight = data.height;
}
if(res.event == 'progressChange') {
this.progress = res.data * 1
}
if(res.event == 'file') {
this.tempFilePath.push(res.data)
if(this.tempFilePath.length > 7) {
this.tempFilePath.shift()
}
return
}
if(res.event == 'success') {
if(res.data) {
this.tempFilePath.push(res.data)
if(this.tempFilePath.length > 8) {
this.tempFilePath.shift()
}
if(this.isCanvasToTempFilePath) {
this.setFilePath(this.tempFilePath.join(''), true)
}
} else {
this.$emit('fail')
}
return
}
this.$emit(res.event, JSON.parse(res.data));
} else if (res?.file) {
this.file = res.data;
} else {
console.error(res);
}
},
async webViewInit() {
await sleep(30)
// await this.getWebViewInited()
const webview = this.$refs.webview;
webview.evalJS(painterScript)
},
getWebViewInited() {
if(this.inited) return Promise.resolve(this.inited);
return new Promise((resolve) => {
this.$watch(
'inited',
async val => {if(val) {resolve(val)}},
{immediate: true}
);
})
},
getTempFilePath() {
if(this.tempFilePath.length == 8) return Promise.resolve(this.tempFilePath)
return new Promise((resolve) => {
this.$watch(
'tempFilePath',
async val => {if(val.length == 8) {resolve(val.join(''))}}
);
})
},
getWebViewDone() {
if(this.progress == 1) return Promise.resolve(this.progress);
return new Promise((resolve) => {
this.$watch(
'progress',
async val => {
if(val == 1) {
this.$emit('done')
resolve(val)
}},
{immediate: true}
);
})
},
async render(args) {
const newNode = await this.calcImage(args);
await this.getWebViewInited()
const webview = this.$refs.webview;
webview.evalJS(`source(${JSON.stringify(newNode)})`)
if(this.isCanvasToTempFilePath) {
await this.getWebViewDone()
await sleep(this.afterDelay)
const params = {fileType: this.fileType, quality: this.quality}
webview.evalJS(`save(${JSON.stringify(params)})`)
}
},
async calcImage(args) {
let node = JSON.parse(JSON.stringify(args))
const url = node.url || node.src
if(node.type === "image" && url && !isBase64(url)) {
const suffix = url.match(/\.(\w+)$/)[1]
const {width = 0, height = 0, path, naturalSrc} = await getImageInfo(url)
const src = await pathToBase64(path)
node.src = src.replace(/^data:application[\w\/]+;base64/,'data:image/'+suffix+';base64')
} else if(node.views?.length) {
for (let i = 0; i < node.views.length; i++) {
node.views[i] = await this.calcImage(node.views[i])
}
}
return node
},
async canvasToTempFilePath(args = {}){
this.tempFilePath = []
this.$refs.webview.evalJS(`save(${JSON.stringify(args)})`)
try{
let tempFilePath = await this.getTempFilePath()
tempFilePath = await this.setFilePath(tempFilePath)
args.success({errMsg: "canvasToTempFilePath:ok", tempFilePath})
}catch(e){
args.fail({error: e})
}
}
// #endif
// #ifndef APP-NVUE
getParentWeith() {
uni.createSelectorQuery()
.in(this)
.select(`.lime-painter`)
.boundingClientRect()
.exec(res => {
this.canvasWidth = Math.ceil(res[0].width)
this.canvasHeight = res[0].height
})
},
async update(args, single) {
this.painter = null;
// #ifdef MP-WEIXIN
if (this.use2dCanvas) {
this.ctx = null;
this.inited = false;
}
// #endif
// #ifdef MP-ALIPAY
this.inited = false;
// #endif
this.isDrawIng = false;
await new Promise(resolve => this.$nextTick(resolve));
await sleep(200);
return this.render(args, single);
},
async render(args = {}, single = false) {
if (this.isDrawIng) {
return this.update(args, single);
}
this.isDrawIng = true;
const isArg = JSON.stringify(args) != '{}';
const ctx = await this.getContext();
let { use2dCanvas, boardWidth, boardHeight, canvas, afterDelay } = this;
if (use2dCanvas && !canvas) {
return Promise.reject(new Error('render: fail canvas has not been created'));
}
this.boundary = {
top: 0,
left: 0,
width: boardWidth,
height: boardHeight
};
// if (!single) {
// ctx.clearRect(0, 0, boardWidth, boardHeight);
// }
if(!this.painter) {
this.painter = new Painter({context: ctx, canvas, width: boardWidth, height: boardHeight, pixelRatio: this.dpr}, this)
}
this.painter.listen('progressChange', (v) => {
this.$emit('progress', v)
})
const {width, height} = await this.painter.source(args)
this.canvasHeight = toPx(this.el.css?.height) || height
this.canvasWidth = toPx(this.el.css?.width) || width
this.boundary.height = this.canvasHeight
this.boundary.width = this.canvasWidth
await sleep(this.sleep);
await this.painter.render()
await new Promise(resolve => this.$nextTick(resolve));
if (!use2dCanvas && !single) {
await this.canvasDraw();
}
if (afterDelay && use2dCanvas) {
await sleep(afterDelay);
}
this.$emit('done');
if (this.isCanvasToTempFilePath && !single && this.isDrawIng) {
this.canvasToTempFilePath()
.then(async res => {
this.$emit('success', res.tempFilePath)
})
.catch(err => {
this.$emit('fail', new Error(JSON.stringify(err)));
});
}
this.isDrawIng = false;
return Promise.resolve({ ctx, draw: this.painter, node: this.node });
},
async custom(cb) {
const { ctx, draw } = await this.render({}, true);
ctx.save();
await cb(ctx, draw);
ctx.restore();
return Promise.resolve(true);
},
async single(args = {}) {
const res = await this.render(args, true);
return Promise.resolve(res);
},
canvasDraw(flag = false) {
return new Promise((resolve, reject) => this.ctx.draw(flag, () => setTimeout(() => resolve(), this.afterDelay)));
},
async getContext() {
if (this.ctx && this.inited) {
return Promise.resolve(this.ctx);
}
this.getParentWeith()
const { type, use2dCanvas, dpr, boardWidth, boardHeight } = this;
const _getContext = () => {
return new Promise(resolve => {
uni.createSelectorQuery()
.in(this)
.select(`#${this.canvasId}`)
.boundingClientRect()
.exec(res => {
if (res) {
const ctx = uni.createCanvasContext(this.canvasId, this);
if (!this.inited) {
this.inited = true;
this.use2dCanvas = false;
this.canvas = res;
}
if(this.isPC) {
ctx.scale(1/dpr, 1/dpr);
}
// #ifdef MP-ALIPAY
ctx.scale(dpr, dpr);
// #endif
this.ctx = ctx
resolve(this.ctx);
}
});
});
};
// #ifndef MP-WEIXIN
return _getContext();
// #endif
if (!use2dCanvas) {
return _getContext();
}
return new Promise(resolve => {
uni.createSelectorQuery()
.in(this)
.select(`#${this.canvasId}`)
.node()
.exec(res => {
let { node: canvas } = res[0];
if (!canvas) {
this.use2dCanvas = false;
resolve(this.getContext());
}
const ctx = canvas.getContext(type);
if (!this.inited) {
this.inited = true;
this.use2dCanvas = true;
this.canvas = canvas;
}
this.ctx = ctx
resolve(this.ctx);
});
});
},
canvasToTempFilePath(args = {}) {
const { use2dCanvas, canvasId, dpr, fileType, quality } = this;
return new Promise((resolve, reject) => {
let { top: y = 0, left: x = 0, width, height } = this.boundary || this;
let destWidth = width * dpr;
let destHeight = height * dpr;
// #ifdef MP-ALIPAY
width = destWidth;
height = destHeight;
// #endif
const success = async (res) => {
const tempFilePath = await this.setFilePath(res.tempFilePath)
resolve(Object.assign(res, {tempFilePath}))
}
const copyArgs = Object.assign({
x,
y,
width,
height,
destWidth,
destHeight,
canvasId,
fileType,
quality,
success,
fail: reject
}, args);
if (use2dCanvas) {
delete copyArgs.canvasId;
copyArgs.canvas = this.canvas;
}
uni.canvasToTempFilePath(copyArgs, this);
});
}
// #endif
}
};
</script>
<style>
.lime-painter, .lime-painter__canvas {
// #ifndef APP-NVUE
width: 100%;
// #endif
// #ifdef APP-NVUE
flex: 1;
// #endif
}
</style>

View File

@ -0,0 +1,103 @@
const painterContent = `
var cache = [];
var painter = null;
var canvas = null;
var context = null;
var timer = null;
var pixelRatio = 1;
console.log = function(...args) {
postMessage(args);
};
function stringify(key, value) {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
return;
}
cache.push(value);
}
return value;
};
function emit(event, data) {
let dataStr = typeof data !== 'object' && data !== null ? data : JSON.stringify(data, stringify);
postMessage({
event,
data: dataStr
});
cache = [];
};
function postMessage(data) {
uni.postMessage({
data
});
};
function init() {
canvas = document.querySelector('#lime-painter');
context = canvas.getContext('2d');
pixelRatio = window.devicePixelRatio;
painter = new Painter({
id: 'lime-painter',
context,
canvas,
pixelRatio,
width: canvas.offsetWidth,
height: canvas.offsetHeight
});
emit('inited', true);
painter.listen('progressChange', (v) => {
emit('progressChange', v);
});
};
function save(args) {
delete args.success;
delete args.fail;
clearTimeout(timer);
timer = setTimeout(() => {
const path = painter.save(args);
if(typeof path == 'string') {
const index = Math.ceil(path.length / 8);
for (var i = 0; i < 8; i++) {
if(i == 7) {
emit('success', path.substr(i * index, index));
} else {
emit('file', path.substr(i * index, index));
}
};
} else {
emit('fail', '');
};
}, 30);
};
async function source(args) {
let res = await painter.source(args);
emit('layoutChange', res);
await painter.render();
};
`
export default `
document.write("<canvas id='lime-painter'>不支持cavnas</canvas>");
let meta = document.createElement('meta');
meta.name = 'viewport';
meta.content = 'width=device-width, initial-scale=1.0';
document.head.appendChild(meta);
let styleEl = document.createElement('style');
styleEl.setAttribute('type', 'text/css');
styleEl.textContent='html,body,#lime-painter{padding: 0; margin: 0; width:100%;height:100%}';
document.head.appendChild(styleEl);
var script = document.createElement("script");
script.language = "javascript";
script.src = "https://js.cdn.aliyun.dcloud.net.cn/dev/uni-app/uni.webview.1.5.2.js";
script.onload = function() {
var script = document.createElement("script");
script.language = "javascript";
script.src = "https://cdn.jsdelivr.net/gh/liangei/image@latest/lime-ui/lime-painter/painter.js";
script.onload = function() {init()};
document.head.appendChild(script);
};
document.head.appendChild(script);
var script = document.createElement("script");
script.language = "javascript";
script.text = "${painterContent}";
document.body.appendChild(script);
`

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,414 @@
const networkReg = /^(http|\/\/)/;
export const isBase64 = (path) => /^data:image\/(\w+);base64/.test(path);
export function sleep(delay) {
return new Promise(resolve => setTimeout(resolve, delay))
}
const isDev = ['devtools'].includes(uni.getSystemInfoSync().platform)
// 缓存图片
let cache = {}
export function isNumber(value) {
return /^-?\d+(\.\d+)?$/.test(value);
}
export function toPx(value, baseSize, isDecimal = false) {
// 如果是数字
if (typeof value === 'number') {
return value
}
// 如果是字符串数字
if (isNumber(value)) {
return value * 1
}
// 如果有单位
if (typeof value === 'string') {
const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g
const results = reg.exec(value);
if (!value || !results) {
return 0;
}
const unit = results[3];
value = parseFloat(value);
let res = 0;
if (unit === 'rpx') {
res = uni.upx2px(value);
} else if (unit === 'px') {
res = value * 1;
} else if (unit === '%') {
res = value * toPx(baseSize) / 100;
} else if (unit === 'em') {
res =value * toPx(baseSize || 14);
}
return isDecimal ? res.toFixed(2) * 1 : Math.round(res);
}
return 0
}
// 计算版本
export function compareVersion(v1, v2) {
v1 = v1.split('.')
v2 = v2.split('.')
const len = Math.max(v1.length, v2.length)
while (v1.length < len) {
v1.push('0')
}
while (v2.length < len) {
v2.push('0')
}
for (let i = 0; i < len; i++) {
const num1 = parseInt(v1[i], 10)
const num2 = parseInt(v2[i], 10)
if (num1 > num2) {
return 1
} else if (num1 < num2) {
return -1
}
}
return 0
}
// #ifdef MP
export const prefix = () => {
// #ifdef MP-TOUTIAO
return tt
// #endif
// #ifdef MP-WEIXIN
return wx
// #endif
// #ifdef MP-BAIDU
return swan
// #endif
// #ifdef MP-ALIPAY
return my
// #endif
// #ifdef MP-QQ
return qq
// #endif
// #ifdef MP-360
return qh
// #endif
}
// #endif
const base64ToArrayBuffer = (data) => {
// #ifndef MP-WEIXIN || APP-PLUS
/**
* Base64Binary.decode(base64_string);
* Base64Binary.decodeArrayBuffer(base64_string);
*/
const Base64Binary = {
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
/* will return a Uint8Array type */
decodeArrayBuffer(input) {
const bytes = (input.length/4) * 3;
const ab = new ArrayBuffer(bytes);
this.decode(input, ab);
return ab;
},
removePaddingChars(input) {
const lkey = this._keyStr.indexOf(input.charAt(input.length - 1));
if(lkey == 64){
return input.substring(0,input.length - 1);
}
return input;
},
decode(input, arrayBuffer) {
//get last chars to see if are valid
input = this.removePaddingChars(input);
input = this.removePaddingChars(input);
const bytes = parseInt((input.length / 4) * 3, 10);
let uarray;
let chr1, chr2, chr3;
let enc1, enc2, enc3, enc4;
let i = 0;
let j = 0;
if (arrayBuffer)
uarray = new Uint8Array(arrayBuffer);
else
uarray = new Uint8Array(bytes);
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
for (i=0; i<bytes; i+=3) {
//get the 3 octects in 4 ascii chars
enc1 = this._keyStr.indexOf(input.charAt(j++));
enc2 = this._keyStr.indexOf(input.charAt(j++));
enc3 = this._keyStr.indexOf(input.charAt(j++));
enc4 = this._keyStr.indexOf(input.charAt(j++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
uarray[i] = chr1;
if (enc3 != 64) uarray[i+1] = chr2;
if (enc4 != 64) uarray[i+2] = chr3;
}
return uarray;
}
}
return Base64Binary.decodeArrayBuffer(data)
// #endif
// #ifdef MP-WEIXIN || APP-PLUS
return uni.base64ToArrayBuffer(data)
// #endif
}
/**
* base64转路径
* @param {Object} base64
*/
export function base64ToPath(base64) {
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64) || [];
return new Promise((resolve, reject) => {
// #ifdef MP
const fs = uni.getFileSystemManager()
//自定义文件名
if (!format) {
console.error('ERROR_BASE64SRC_PARSE')
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
const time = new Date().getTime();
let pre = prefix()
const filePath = `${pre.env.USER_DATA_PATH}/${time}.${format}`
//let buffer = base64ToArrayBuffer(bodyData)
console.log(filePath)
fs.writeFile({
filePath,
data: base64.replace(/^data:\S+\/\S+;base64,/, ''),
encoding: 'base64',
// data: buffer,
// encoding: 'binary',
success() {
resolve(filePath)
},
fail(err) {
console.log(base64,'!!!!!!')
console.error('获取base64图片失败', JSON.stringify(err))
reject(err)
}
})
// #endif
// #ifdef H5
// mime类型
let mimeString = base64.split(',')[0].split(':')[1].split(';')[0];
//base64 解码
let byteString = atob(base64.split(',')[1]);
//创建缓冲数组
let arrayBuffer = new ArrayBuffer(byteString.length);
//创建视图
let intArray = new Uint8Array(arrayBuffer);
for (let i = 0; i < byteString.length; i++) {
intArray[i] = byteString.charCodeAt(i);
}
resolve(URL.createObjectURL(new Blob([intArray], { type: mimeString })))
// #endif
// #ifdef APP-PLUS
const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now())
bitmap.loadBase64Data(base64, () => {
if (!format) {
reject(new Error('ERROR_BASE64SRC_PARSE'))
}
const time = new Date().getTime();
const filePath = `_doc/uniapp_temp/${time}.${format}`
bitmap.save(filePath, {},
() => {
bitmap.clear()
resolve(filePath)
},
(error) => {
bitmap.clear()
console.error(`${JSON.stringify(error)}`)
reject(error)
})
}, (error) => {
bitmap.clear()
console.error(`${JSON.stringify(error)}`)
reject(error)
})
// #endif
})
}
/**
* 路径转base64
* @param {Object} string
*/
export function pathToBase64(path) {
if(/^data:/.test(path)) return path
return new Promise((resolve, reject) => {
// #ifdef H5
const _canvas = ()=> {
let image = new Image();
image.setAttribute("crossOrigin",'Anonymous');
image.onload = function() {
let canvas = document.createElement('canvas');
// 获取图片原始宽高
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
// 将图片插入画布并开始绘制
canvas.getContext('2d').drawImage(image, 0, 0);
let result = canvas.toDataURL('image/png')
resolve(result);
canvas.height = canvas.width = 0
}
image.src = path
image.onerror = (error) => {
console.error(`urlToBase64 error: ${path}`, JSON.stringify(error))
reject(new Error('urlToBase64 error'));
};
}
const _fileReader = (blob) => {
const fileReader = new FileReader();
fileReader.onload = (e) => {
resolve(e.target.result);
};
fileReader.readAsDataURL(blob);
fileReader.onerror = (error) => {
console.error('blobToBase64 error:', JSON.stringify(error))
reject(new Error('blobToBase64 error'));
};
}
const isFileReader = typeof FileReader === 'function'
if(networkReg.test(path) && isFileReader ) {
window.URL = window.URL || window.webkitURL;
const xhr = new XMLHttpRequest();
xhr.open("get", path, true);
xhr.timeout = 2000;
xhr.responseType = "blob";
xhr.onload = function() {
if(this.status == 200) {
_fileReader(this.response)
} else {
_canvas()
}
}
xhr.onreadystatechange = function() {
if(this.status === 0) {
console.error('图片跨域了,得后端处理咯')
}
}
xhr.send();
} else if(/^blob/.test(path) && isFileReader){
_fileReader(path)
} else {
_canvas()
}
// #endif
// #ifdef MP
if(uni.canIUse('getFileSystemManager')) {
uni.getFileSystemManager().readFile({
filePath: path,
encoding: 'base64',
success: (res) => {
resolve('data:image/png;base64,' + res.data)
},
fail: (error) => {
console.error('urlToBase64 error:', JSON.stringify(error))
reject(error)
}
})
}
// #endif
// #ifdef APP-PLUS
plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => {
entry.file((file) => {
const fileReader = new plus.io.FileReader()
fileReader.onload = (data) => { resolve(data.target.result)}
fileReader.onerror = (error) => {
console.error('pathToBase64 error:', JSON.stringify(error))
reject(error)
}
fileReader.readAsDataURL(file)
}, (error) => {
console.error('pathToBase64 error:', JSON.stringify(error))
reject(error)
})
}, (error) => {
console.error('pathToBase64 error:', JSON.stringify(error))
reject(error)
})
// #endif
})
}
// #ifdef APP-PLUS
const getLocalFilePath = (path)=> {
if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path.indexOf('_downloads') === 0) {
return path
}
if (path.indexOf('file://') === 0) {
return path
}
if (path.indexOf('/storage/emulated/0/') === 0) {
return path
}
if (path.indexOf('/') === 0) {
const localFilePath = plus.io.convertAbsoluteFileSystem(path)
if (localFilePath !== path) {
return localFilePath
} else {
path = path.substr(1)
}
}
return '_www/' + path
}
// #endif
export function getImageInfo(img, isH5PathToBase64 = false) {
return new Promise(async (resolve, reject) => {
// const base64Reg = /^data:image\/(\w+);base64/
const localReg = /^\.|^\/(?=[^\/])/;
// #ifdef H5
if(networkReg.test(img) && isH5PathToBase64) {
img = await pathToBase64(img)
}
// #endif
// #ifndef MP-ALIPAY
if(isBase64(img)) {
if(isDev || !cache[img]) {
const imgName = img
img = await base64ToPath(img)
cache[imgName] = img
} else {
img = cache[img]
}
}
// #endif
if(cache[img] && cache[img].errMsg) {
resolve(cache[img])
} else {
uni.getImageInfo({
src: img,
success: (image) => {
// #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO
image.path = localReg.test(img) ? `/${image.path}` : image.path;
// #endif
// #ifdef H5
image.path = image.path.replace(/^\./, window.location.origin)
// #endif
image.naturalSrc = img
if(isDev) {
resolve(image)
} else {
cache[img] = image
resolve(cache[img])
}
},
fail(err) {
resolve({path: img})
console.error(`getImageInfo:fail ${img} failed ${JSON.stringify(err)}`);
}
})
}
})
}