You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

788 lines
20 KiB
Vue

1 year ago
<template>
<view class="tm-render" @touchmove.stop.prevent="">
<!-- #ifdef MP-WEIXIN || MP-QQ || MP-KUAISHOU || MP-ALIPAY -->
<canvas :style="{width:`${c_w}px`,height:`${c_h}px`}" @mouseup="touchend" @mousedown="touchstart"
@mousemove="touchmove" @touchend="touchend" @touchmove="touchmove" @touchstart="touchstart" id="exid"
canvas-id="exid" type="2d"></canvas>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN || MP-QQ || MP-KUAISHOU || MP-ALIPAY || APP-PLUS -->
<canvas :style="{width:`${c_w}px`,height:`${c_h}px`}"
@mouseup="touchend"
@mousedown="touchstart"
@mousemove="touchmove"
@touchend="touchend"
@touchmove="touchmove"
@touchstart="touchstart"
id="exid"
canvas-id="exid" ></canvas>
<!-- #endif -->
<!-- #ifdef APP-PLUS-->
<canvas
@mouseup="CRender.touchend_app"
@mousedown="CRender.touchstart_app"
@mousemove="CRender.touchmove_app"
@touchend="CRender.touchend_app"
@touchmove="CRender.touchmove_app"
@touchstart="CRender.touchstart_app"
:prop="app_opts" :change:prop="CRender.update_app_opts" :style="{width:`${c_w}px`,height:`${c_h}px`}" id="exid" canvas-id="exid" ></canvas>
<!-- #endif -->
</view>
</template>
<script>
// #ifndef APP-PLUS
import CRender from '@/tm-vuetify/tool/function/crender/class/crender.class'
// #endif
let render = null;
export default {
name: "tm-render",
props: {
width: {
type: Number,
default: 0
},
height: {
type: Number,
default: 600
}
},
computed: {
c_w: {
get: function() {
return this.cavan_width;
},
set: function(val) {
this.cavan_width = val;
},
},
c_h: function() {
return uni.upx2px(this.height);
}
},
created() {
const dpr = uni.getSystemInfoSync().pixelRatio
this.dpr = dpr;
this.c_w = uni.upx2px(this.width);
},
data() {
return {
cavan_width: 0,
canvaConfig: null,
dragGrpahId: '',//当前正在拖动或者点击的项目id.
old_x: 0,
old_y: 0,
isDrag: false,
dpr: 1,
app_opts:{
render_app:null,
graph:null,
fun:[],
canvaConfig:null
}
};
},
mounted() {
this.$nextTick(async function() {
// #ifndef APP-PLUS
this.inits();
// #endif
// #ifdef APP-PLUS
let res = await this.$Querey('.tm-render', this).catch(e => {})
let p = res[0];
this.$set(this.app_opts,'canvaConfig',p)
// #endif
})
},
destroyed() {
render = null;
clearTimeout(555)
},
methods: {
async inits() {
let t = this;
let res = await this.$Querey('.tm-render', this).catch(e => {})
let p = res[0];
t.c_w = p.width || 300;
t.canvaConfig = p;
//#ifdef MP-WEIXIN || MP-QQ || MP-KUAISHOU
uni.createSelectorQuery().in(t).select('#exid').fields({
node: true,
context: true,
}, function(res) {
let canvas = res.node;
let ctx = canvas.getContext('2d')
ctx['dpr'] = t.dpr;
ctx['scaledpr'] = 10 / (10 * t.dpr);
const w = ctx['width'] = t.c_w
const h = ctx['height'] = t.c_h
// canvas.width = res[0].width * dpr
// canvas.height = res[0].height * dpr
const dpr = uni.getSystemInfoSync().pixelRatio
canvas.width = w * dpr
canvas.height = h * dpr
// 设置 canvas 坐标原点
ctx.translate(0, 0);
ctx.scale(dpr, dpr)
render = new CRender(ctx, t, canvas)
t.$nextTick(function() {
// t.$emit('render', render.area);
// 12296升级改造
t.$emit('render', render.area);
})
}).exec()
//#endif
//#ifndef MP-WEIXIN || MP-QQ || MP-KUAISHOU || MP-ALIPAY || APP-PLUS
let ctx = uni.createCanvasContext('exid', t)
ctx['dpr'] = t.dpr;
ctx['scaledpr'] = 10 / (10 * t.dpr);
const w = ctx['width'] = t.c_w
const h = ctx['height'] = t.c_h
render = new CRender(ctx, t)
t.$nextTick(function() {
// 12296升级改造
t.$emit('render', render.area);
// t.$emit('render', render.area);
})
//#endif
},
wait(time) {
return new Promise(resolve => setTimeout(resolve, time))
},
getTextWidthAndPos(shape) {
// #ifdef APP-PLUS
return [0,0,0,0];
// #endif
if (!render) return [0, 0, 0, 0];
let {
content,
position,
maxWidth,
rowGap
} = shape.shape
const {
textBaseline,
fontSize,
textAlign
} = shape.style
let [x, y] = position
content = content.split('\n')
const rowNum = content.length
const lineHeight = fontSize + rowGap
const allHeight = rowNum * lineHeight - rowGap
const twidth = render.ctx.measureText(content + "").width;
if (textBaseline === 'middle') {
y -= allHeight * rowNum + fontSize / 2
}
if (textBaseline === 'bottom') {
y += fontSize
}
if (textAlign === 'center') {
x -= twidth / 2
y += fontSize
}
return [x, y, twidth, allHeight]
// measureText
},
getRender() {
// #ifdef APP-PLUS
return this.app_opts.render_app;
// #endif
return render;
},
renderCavans(e){
render = e;
//app触发这里。
this.$emit('render', render.area);
},
//app-plus代理执行函数参数一为函数名称参数二为参数。
appCallFun(funName,arg){
// #ifdef APP-PLUS
this.$set( this.app_opts,'fun',[funName,arg])
// #endif
// #ifndef APP-PLUS
console.error("此函数为APP-PLUS专用详见https://jx2d.cn/guid/render/%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8.html")
// #endif
},
async addGraph(obj) {
let t = this;
let pf = obj;
// #ifdef APP-PLUS
this.$set( this.app_opts,'graph',obj)
return
// #endif
if (typeof obj == 'object' && Array.isArray(obj)) {
let c = obj.filter(el => {
return {
...el,
tmid: uni.$tm.guid()
};
})
pf = c;
} else if (typeof obj == 'object' && !Array.isArray(obj)) {
pf = [{
...pf,
tmid: uni.$tm.guid()
}]
}
let graphs = pf.map(config => render.add(config))
graphs.forEach((graph, i) => {
const config = pf[i]
t.updateGraphConfigByKey(graph, config)
})
await render.launchAnimation()
//释放内存。
// graphs = []
return graphs.length == 1 ? graphs[0] : graphs;
},
//添加完毕需要更新下,才会显示。
updateGraphConfigByKey(graph, config) {
const keys = Object.keys(config)
keys.forEach(async key => {
if (key === 'shape' || key === 'style') {
// graph.animation('shape', {x:config.shape.x+5}, true)
await graph.animation(key, config[key], true)
} else {
graph[key] = config[key]
}
})
},
touchend(event) {
let t = this;
let evx = 0;
let evy = 0;
//触摸
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
evx = event.changedTouches[0].x
evy = event.changedTouches[0].y
//电脑端。
} else {
evx = event.pageX - this.canvaConfig.left
evy = event.pageY - this.canvaConfig.top;
}
let x = evx;
let y = evy;
this.dragGrpahId = "";
this.isDrag = false
//触发画板的事件。
this.$emit('touchend', {
x: x,
y: y
})
//在那个元素上离开的。
let gps = render.graphs;
let isClickGrpahs = gps.filter((el, index) => {
if (el.name == 'text') {
let rect = t.getTextWidthAndPos(el);
el.hoverRect = rect
}
return el.hoverCheck([x, y], el) || el.hoverCheckProcessor([x, y], el);
});
if (isClickGrpahs.length > 0) {
let nowgap = isClickGrpahs[0];
// 执行元素上绑定的事件。
if (nowgap[event.type]) nowgap[event.type].call(nowgap, {
x: x,
y: y
});
}
},
touchmove(event) {
let t = this;
let evx = 0;
let evy = 0;
let isPc = false
//触摸
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
evx = event.changedTouches[0].x
evy = event.changedTouches[0].y
isPc = false
//电脑端。
} else {
evx = event.pageX - this.canvaConfig.left
evy = event.pageY - this.canvaConfig.top;
isPc = true;
}
let movex = evx - this.old_x;
let movey = evy - this.old_y;
let x = evx;
let y = evy;
// 触发发画板的事件
this.$emit('touchmove', {
x: x,
y: y
})
if(this.isDrag==false) return;
//在哪个元素移动的。
let gps = render.graphs;
let isClickGrpahs = gps.filter((el, index) => {
return (el.hoverCheck([x, y], el) || el.hoverCheckProcessor([x, y], el))&&el.tmid==t.dragGrpahId;
});
if (isClickGrpahs.length > 0) {
let nowgap = isClickGrpahs[0];
if (isPc) {
movex = evx - this.old_x;
movey = evy - this.old_y;
}
if ((nowgap.drag === true && this.isDrag == true) || (nowgap.drag === true && isPc == false)) {
if (nowgap.name == "circle" || nowgap.name == "ellipse" ||
nowgap.name == "ring" || nowgap.name == "arc" || nowgap.name == "regPolygon") {
nowgap.attr('shape', {
rx: movex,
ry: movey
})
} else if (nowgap.name == "rect" ||nowgap.name == 'rectRound'|| nowgap.name == "path"|| nowgap.name == "image"|| nowgap.name == "star" || nowgap.name =='arrow') {
nowgap.attr('shape', {
x: movex,
y: movey
})
} else if (nowgap.name == "text") {
nowgap.attr('shape', {
position: [movex, movey]
})
}
// 执行元素上绑定的事件。
if (nowgap[event.type]) nowgap[event.type].call(nowgap, {
x: movex,
y: movey
});
// if(nowgap['mousemove']||nowgap['touchmove']){
// if (nowgap['mousemove']) nowgap.mousemove.call(nowgap,{x:movex,y:movey})
// if (nowgap['touchmove']) nowgap.touchmove.call(nowgap,{x:movex,y:movey})
// }
}
//配置不允许拖出边界。
// if(this.dragGrpahId === nowgap.tmid
// && movex+nowgap.shape.w<this.canvaConfig.width
// && movey+nowgap.shape.h<this.canvaConfig.height
// && x>=0&&y>=0&&movex>=0&&movey>=0
// ){
// }
// this.$emit('shape:touchmove',{x:x,y:y,shape:nowgap})
}
},
touchstart(event) {
let t = this;
let evx = 0;
let evy = 0;
let isPc = false
//触摸
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
evx = event.changedTouches[0].x
evy = event.changedTouches[0].y
isPc = false
//电脑端。
} else {
evx = event.pageX - this.canvaConfig.left
evy = event.pageY - this.canvaConfig.top
isPc = true;
}
let x = evx
let y = evy
let gps = render.graphs;
//点中了哪些图片,第一个是最顶层的,依次类推。
let isClickGrpahs = gps.filter((el, index) => {
// 要判断谁的层级高就是先托动谁。
return el.hoverCheck([x, y], el) || el.hoverCheckProcessor([x, y], el);
});
if (isClickGrpahs.length > 0) {
var indexOfMax = 0;
var max = isClickGrpahs.reduce( (a,c,i) => c.index > a ? (indexOfMax = i,c.index) : a, 0)
let nowgap = isClickGrpahs[indexOfMax];
if (nowgap.drag === true) {
this.dragGrpahId = nowgap.tmid;
let gapPos = [];
if (nowgap.name == "circle" || nowgap.name == "ellipse" ||
nowgap.name == "ring" || nowgap.name == "arc" || nowgap.name == "regPolygon"
) {
gapPos = [nowgap.shape.rx, nowgap.shape.ry]
} else if (nowgap.name == "rect" ||nowgap.name == 'rectRound'|| nowgap.name == "path"|| nowgap.name == "image"|| nowgap.name == "star"|| nowgap.name =='arrow') {
gapPos = [nowgap.shape.x, nowgap.shape.y]
} else if (nowgap.name == "text") {
gapPos = nowgap.shape.position
}
if (isPc) {
this.old_x = evx - gapPos[0]
this.old_y = evy - gapPos[1];
} else {
this.old_x = x - gapPos[0];
this.old_y = y - gapPos[1];
}
this.isDrag = true
}
// 执行元素上绑定的事件。
if (nowgap[event.type]) nowgap[event.type].call(nowgap, {
x: x,
y: y
});
} else {
this.dragGrpahId = ""
}
this.$emit('touchstart', {
x: x,
y: y
})
},
},
}
</script>
<script module="CRender" lang="renderjs">
var cdr = require('@/tm-vuetify/tool/function/crender/crender.min.js')
const CRender = window.CRender.CRender
var render_app;
export default {
mounted() {
// #ifdef APP-PLUS
this.$nextTick(function(){
this.initsAppH5();
})
// #endif
},
methods: {
initsAppH5(e) {
let t = this;
let canvas = document.querySelector('#exid').querySelector('canvas')
let canvasPrent = document.querySelector('.tm-render')
let w = canvasPrent.offsetWidth;
let h = canvasPrent.offsetHeight;
canvas.style.width = w+'px';
canvas.style.height = h+'px';
render_app = new CRender(canvas)
this.$ownerInstance.$vm.$set(this.$ownerInstance.$vm.app_opts,'render_app',render_app)
setTimeout(function() {
t.$ownerInstance.callMethod('renderCavans', render_app)
}, 10);
},
update_app_opts(newValue, oldValue, ownerInstance, instance) {
if(newValue.graph){
let graph = render_app.add(newValue.graph)
this.$ownerInstance.$vm.app_opts.graph=null;
}
if(newValue.fun){
if(typeof(newValue.fun)=="object" && Array.isArray(newValue.fun)){
if(newValue.fun.length>0){
if(newValue.fun.length==1){
render_app[newValue.fun[0]]();
}else if(newValue.fun.length==2){
render_app[newValue.fun[0]](newValue.fun[1])
}
}
}
}
},
wait(time) {
return new Promise(resolve => setTimeout(resolve, time))
},
getTextWidthAndPos(shape) {
// #ifdef APP-PLUS
return [0,0,0,0];
// #endif
if (!render) return [0, 0, 0, 0];
let {
content,
position,
maxWidth,
rowGap
} = shape.shape
const {
textBaseline,
fontSize,
textAlign
} = shape.style
let [x, y] = position
content = content.split('\n')
const rowNum = content.length
const lineHeight = fontSize + rowGap
const allHeight = rowNum * lineHeight - rowGap
const twidth = render.ctx.measureText(content + "").width;
if (textBaseline === 'middle') {
y -= allHeight * rowNum + fontSize / 2
}
if (textBaseline === 'bottom') {
y += fontSize
}
if (textAlign === 'center') {
x -= twidth / 2
y += fontSize
}
return [x, y, twidth, allHeight]
// measureText
},
touchend_app(event) {
let t = this;
let evx = 0;
let evy = 0;
//触摸
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
evx = event.changedTouches[0].x
evy = event.changedTouches[0].y
//电脑端。
} else {
evx = event.pageX - this.app_opts.canvaConfig.left
evy = event.pageY - this.app_opts.canvaConfig.top;
}
let x = evx;
let y = evy;
this.dragGrpahId = "";
this.isDrag = false
//触发画板的事件。
this.$emit('touchend', {
x: x,
y: y
})
//在那个元素上离开的。
let gps = render_app.graphs;
let isClickGrpahs = gps.filter((el, index) => {
if (el.name == 'text') {
let rect = t.getTextWidthAndPos(el);
el.hoverRect = rect
}
return el.hoverCheck([x, y], el) || el.hoverCheckProcessor([x, y], el);
});
if (isClickGrpahs.length > 0) {
let nowgap = isClickGrpahs[0];
// 执行元素上绑定的事件。
if (nowgap[event.type]) nowgap[event.type].call(nowgap, {
x: x,
y: y
});
}
},
touchmove_app(event) {
let t = this;
let evx = 0;
let evy = 0;
let isPc = false
//触摸
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
evx = event.changedTouches[0].x
evy = event.changedTouches[0].y
isPc = false
//电脑端。
} else {
evx = event.pageX - this.app_opts.canvaConfig.left
evy = event.pageY - this.app_opts.canvaConfig.top;
isPc = true;
}
let movex = evx - this.old_x;
let movey = evy - this.old_y;
let x = evx;
let y = evy;
// 触发发画板的事件
this.$emit('touchmove', {
x: x,
y: y
})
if(this.isDrag==false) return;
//在哪个元素移动的。
let gps = render_app.graphs;
let isClickGrpahs = gps.filter((el, index) => {
return (el.hoverCheck([x, y], el) || el.hoverCheckProcessor([x, y], el))&&el.tmid==t.dragGrpahId;
});
if (isClickGrpahs.length > 0) {
let nowgap = isClickGrpahs[0];
if (isPc) {
movex = evx - this.old_x;
movey = evy - this.old_y;
}
if ((nowgap.drag === true && this.isDrag == true) || (nowgap.drag === true && isPc == false)) {
if (nowgap.name == "circle" || nowgap.name == "ellipse" ||
nowgap.name == "ring" || nowgap.name == "arc" || nowgap.name == "regPolygon") {
nowgap.attr('shape', {
rx: movex,
ry: movey
})
} else if (nowgap.name == "rect" ||nowgap.name == 'rectRound'|| nowgap.name == "path"|| nowgap.name == "image"|| nowgap.name == "star" || nowgap.name =='arrow') {
nowgap.attr('shape', {
x: movex,
y: movey
})
} else if (nowgap.name == "text") {
nowgap.attr('shape', {
position: [movex, movey]
})
}
// 执行元素上绑定的事件。
if (nowgap[event.type]) nowgap[event.type].call(nowgap, {
x: movex,
y: movey
});
}
}
},
touchstart_app(event) {
let t = this;
let evx = 0;
let evy = 0;
let isPc = false
//触摸
if (event.type.indexOf('mouse') == -1 && event.changedTouches.length == 1) {
evx = event.changedTouches[0].x
evy = event.changedTouches[0].y
isPc = false
//电脑端。
} else {
evx = event.pageX - this.app_opts.canvaConfig.left
evy = event.pageY - this.app_opts.canvaConfig.top
isPc = true;
}
let x = evx
let y = evy
let gps = render_app.graphs;
//点中了哪些图片,第一个是最顶层的,依次类推。
let isClickGrpahs = gps.filter((el, index) => {
return el.hoverCheck([x, y], el) || el.hoverCheckProcessor([x, y], el);
});
if (isClickGrpahs.length > 0) {
var indexOfMax = 0;
var max = isClickGrpahs.reduce( (a,c,i) => c.index > a ? (indexOfMax = i,c.index) : a, 0)
let nowgap = isClickGrpahs[indexOfMax];
if (nowgap.drag === true) {
this.dragGrpahId = nowgap.tmid;
let gapPos = [];
if (nowgap.name == "circle" || nowgap.name == "ellipse" ||
nowgap.name == "ring" || nowgap.name == "arc" || nowgap.name == "regPolygon"
) {
gapPos = [nowgap.shape.rx, nowgap.shape.ry]
} else if (nowgap.name == "rect" ||nowgap.name == 'rectRound'|| nowgap.name == "path"|| nowgap.name == "image"|| nowgap.name == "star"|| nowgap.name =='arrow') {
gapPos = [nowgap.shape.x, nowgap.shape.y]
} else if (nowgap.name == "text") {
gapPos = nowgap.shape.position
}
if (isPc) {
this.old_x = evx - gapPos[0]
this.old_y = evy - gapPos[1];
} else {
this.old_x = x - gapPos[0];
this.old_y = y - gapPos[1];
}
this.isDrag = true
}
// 执行元素上绑定的事件。
if (nowgap[event.type]) nowgap[event.type].call(nowgap, {
x: x,
y: y
});
} else {
this.dragGrpahId = ""
}
this.$emit('touchstart', {
x: x,
y: y
})
},
}
}
</script>
<style lang="scss">
body {}
</style>