Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

能否增加置于底层/置于顶层功能 #1

Open
ymli1981 opened this issue Nov 19, 2018 · 10 comments
Open

能否增加置于底层/置于顶层功能 #1

ymli1981 opened this issue Nov 19, 2018 · 10 comments
Assignees
Labels
help wanted Extra attention is needed

Comments

@ymli1981
Copy link

多个图片重叠,希望能增加置于底层/顶层功能,方便选择拖动

@jasondu
Copy link
Collaborator

jasondu commented Nov 20, 2018

你是说一键底层/顶层的功能吗

@ymli1981
Copy link
Author

对,可以在图片一个角上加个图标,点一下图标,就置顶/置底了

@jasondu
Copy link
Collaborator

jasondu commented Nov 20, 2018

好的,我晚点提供这个api出来

@jasondu jasondu added the help wanted Extra attention is needed label Nov 20, 2018
@jasondu jasondu self-assigned this Nov 20, 2018
@ymli1981
Copy link
Author

还有个问题,能否设置图形选中后,点x才删除?
有时操作某个图形时,会误删其他的图形(没选中它,x不显示,但只要点了它的x所在位置,就会被删除)

@ymli1981
Copy link
Author

误删问题,自己解决了

@PlayerYK
Copy link
Contributor

还有个问题,能否设置图形选中后,点x才删除?
有时操作某个图形时,会误删其他的图形(没选中它,x不显示,但只要点了它的x所在位置,就会被删除)

新版本已经解决了误删问题。

@ming645463631
Copy link

多个图层的时候能不能让选中的图层置顶

@xmcase
Copy link

xmcase commented May 16, 2019

点图置顶功能
把index.js内容替换为以下内容
// components/canvas-drag/index.js
const DELETE_ICON = './icon/close.png'; // 删除按钮
const DRAG_ICON = './icon/scale.png'; // 缩放按钮
const STROKE_COLOR = 'red';
const ROTATE_ENABLED = true;

const DEBUG_MODE = false; // 打开调试后会渲染操作区域边框(无背景时有效)
const dragGraph = function ({x = 30, y = 30, w, h, type, text, fontSize = 20, color = 'red', url = null, rotate = 0, sourceId = null, selected = true,alpha = 1}, canvas, factor) {
if (type === 'text') {
canvas.setFontSize(fontSize);
const textWidth = canvas.measureText(text).width;
const textHeight = fontSize + 10;
this.centerX = x + textWidth / 2;
this.centerY = y + textHeight / 2;
this.w = textWidth;
this.h = textHeight;
} else {
this.centerX = x + w / 2;
this.centerY = y + h / 2;
this.w = w;
this.h = h;
}

this.x = x;
this.y = y;

// 4个顶点坐标
this.square = [
    [this.x, this.y],
    [this.x + this.w, this.y],
    [this.x + this.w, this.y + this.h],
    [this.x, this.y + this.h]
];

this.fileUrl = url;
this.text = text;
this.fontSize = fontSize;
this.color = color;
this.ctx = canvas;
this.rotate = rotate;
this.type = type;
this.selected = selected;
this.factor = factor;
this.alpha = alpha;
this.sourceId = sourceId;
this.MIN_WIDTH = 20;
this.MIN_FONTSIZE = 10;

};

//新加代码******
//数组交换位置,用于点击切换层
const change_pos = function (arr, k, j) {
var c = arr[k];
arr[k] = arr[j];
arr[j] = c;
return arr
};
//新加代码结束****

dragGraph.prototype = {
/**
* 绘制元素
*/
paint() {
this.ctx.save();
// 由于measureText获取文字宽度依赖于样式,所以如果是文字元素需要先设置样式
let textWidth = 0;
let textHeight = 0;
if (this.type === 'text') {
this.ctx.setFontSize(this.fontSize);
this.ctx.setTextBaseline('middle');
this.ctx.setTextAlign('center');
this.ctx.setFillStyle(this.color);
textWidth = this.ctx.measureText(this.text).width;
textHeight = this.fontSize + 10;
// 字体区域中心点不变,左上角位移
this.x = this.centerX - textWidth / 2;
this.y = this.centerY - textHeight / 2;
}

    // 旋转元素
    this.ctx.translate(this.centerX, this.centerY);
    this.ctx.rotate(this.rotate * Math.PI / 180);
    this.ctx.translate(-this.centerX, -this.centerY);
    // 渲染元素
    if (this.type === 'text') {
        this.ctx.fillText(this.text, this.centerX, this.centerY);
    } else if (this.type === 'image') {
		this.ctx.setGlobalAlpha(this.alpha)
        this.ctx.drawImage(this.fileUrl, this.x, this.y, this.w, this.h);
    }
    // 如果是选中状态,绘制选择虚线框,和缩放图标、删除图标
    if (this.selected) {
        this.ctx.setLineDash([2, 5]);
        this.ctx.setLineWidth(2);
        this.ctx.setStrokeStyle(STROKE_COLOR);
        this.ctx.lineDashOffset = 6;

        if (this.type === 'text') {
            this.ctx.strokeRect(this.x, this.y, textWidth, textHeight);
            this.ctx.drawImage(DELETE_ICON, this.x - 15, this.y - 15, 30, 30);
            this.ctx.drawImage(DRAG_ICON, this.x + textWidth - 15, this.y + textHeight - 15, 30, 30);
        } else {
            this.ctx.strokeRect(this.x, this.y, this.w, this.h);
            this.ctx.drawImage(DELETE_ICON, this.x - 15, this.y - 15, 30, 30);
            this.ctx.drawImage(DRAG_ICON, this.x + this.w - 15, this.y + this.h - 15, 30, 30);
        }
    }
    this.ctx.restore();
},
/**
 * 给矩形描边
 * @private
 */
_drawBorder() {
    let p = this.square;
    let ctx = this.ctx;
    this.ctx.save();
    this.ctx.beginPath();
    ctx.setStrokeStyle('orange');
    this._draw_line(this.ctx, p[0], p[1]);
    this._draw_line(this.ctx, p[1], p[2]);
    this._draw_line(this.ctx, p[2], p[3]);
    this._draw_line(this.ctx, p[3], p[0]);
    ctx.restore();
},
/**
 * 画一条线
 * @param ctx
 * @param a
 * @param b
 * @private
 */
_draw_line(ctx, a, b) {
    ctx.moveTo(a[0], a[1]);
    ctx.lineTo(b[0], b[1]);
    ctx.stroke();
},
/**
 * 判断点击的坐标落在哪个区域
 * @param {*} x 点击的坐标
 * @param {*} y 点击的坐标
 */
isInGraph(x, y) {
    // 删除区域左上角的坐标和区域的高度宽度
    const delW = 30;
    const delH = 30;

    // 旋转后的删除区域坐标
    const transformedDelCenter = this._rotatePoint(this.x, this.y, this.centerX, this.centerY, this.rotate);
    const transformDelX = transformedDelCenter[0] - delW / 2;
    const transformDelY = transformedDelCenter[1] - delH / 2;

    // 变换区域左上角的坐标和区域的高度宽度
    const scaleW = 30;
    const scaleH = 30;
    const transformedScaleCenter = this._rotatePoint(this.x + this.w, this.y + this.h, this.centerX, this.centerY, this.rotate);

    // 旋转后的变换区域坐标
    const transformScaleX = transformedScaleCenter[0] - scaleW / 2;
    const transformScaleY = transformedScaleCenter[1] - scaleH / 2;

    // 调试使用,标识可操作区域
    if (DEBUG_MODE) {
        // 标识删除按钮区域
        this.ctx.setLineWidth(1);
        this.ctx.setStrokeStyle('red');
        this.ctx.strokeRect(transformDelX, transformDelY, delW, delH);
        // 标识旋转/缩放按钮区域
        this.ctx.setLineWidth(1);
        this.ctx.setStrokeStyle('black');
        this.ctx.strokeRect(transformScaleX, transformScaleY, scaleW, scaleH);
        // 标识移动区域
        this._drawBorder();
    }

    if (x - transformScaleX >= 0 && y - transformScaleY >= 0 &&
        transformScaleX + scaleW - x >= 0 && transformScaleY + scaleH - y >= 0) {
        // 缩放区域
        return 'transform';
    } else if (x - transformDelX >= 0 && y - transformDelY >= 0 &&
        transformDelX + delW - x >= 0 && transformDelY + delH - y >= 0) {
        // 删除区域
        return 'del';
    } else if (this.insidePolygon(this.square, [x, y])) {
        return 'move';
    }
    // 不在选择区域里面
    return false;
},
/**
 *  判断一个点是否在多边形内部
 *  @param points 多边形坐标集合
 *  @param testPoint 测试点坐标
 *  返回true为真,false为假
 *  */
insidePolygon(points, testPoint) {
    let x = testPoint[0], y = testPoint[1];
    let inside = false;
    for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
        let xi = points[i][0], yi = points[i][1];
        let xj = points[j][0], yj = points[j][1];

        let intersect = ((yi > y) != (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
        if (intersect) inside = !inside;
    }
    return inside;
},
/**
 * 计算旋转后矩形四个顶点的坐标(相对于画布)
 * @private
 */
_rotateSquare() {
    this.square = [
        this._rotatePoint(this.x, this.y, this.centerX, this.centerY, this.rotate),
        this._rotatePoint(this.x + this.w, this.y, this.centerX, this.centerY, this.rotate),
        this._rotatePoint(this.x + this.w, this.y + this.h, this.centerX, this.centerY, this.rotate),
        this._rotatePoint(this.x, this.y + this.h, this.centerX, this.centerY, this.rotate),
    ];
},
/**
 * 计算旋转后的新坐标(相对于画布)
 * @param x
 * @param y
 * @param centerX
 * @param centerY
 * @param degrees
 * @returns {*[]}
 * @private
 */
_rotatePoint(x, y, centerX, centerY, degrees) {
    let newX = (x - centerX) * Math.cos(degrees * Math.PI / 180) - (y - centerY) * Math.sin(degrees * Math.PI / 180) + centerX;
    let newY = (x - centerX) * Math.sin(degrees * Math.PI / 180) + (y - centerY) * Math.cos(degrees * Math.PI / 180) + centerY;
    return [newX, newY];
},
/**
 *
 * @param {*} px 手指按下去的坐标
 * @param {*} py 手指按下去的坐标
 * @param {*} x 手指移动到的坐标
 * @param {*} y 手指移动到的坐标
 * @param {*} currentGraph 当前图层的信息
 */
transform(px, py, x, y, currentGraph) {
    // 获取选择区域的宽度高度
    if (this.type === 'text') {
        this.ctx.setFontSize(this.fontSize);
        const textWidth = this.ctx.measureText(this.text).width;
        const textHeight = this.fontSize + 10;
        this.w = textWidth;
        this.h = textHeight;
        // 字体区域中心点不变,左上角位移
        this.x = this.centerX - textWidth / 2;
        this.y = this.centerY - textHeight / 2;
    } else {
        this.centerX = this.x + this.w / 2;
        this.centerY = this.y + this.h / 2;
    }

    const diffXBefore = px - this.centerX;
    const diffYBefore = py - this.centerY;
    const diffXAfter = x - this.centerX;
    const diffYAfter = y - this.centerY;

    const angleBefore = Math.atan2(diffYBefore, diffXBefore) / Math.PI * 180;
    const angleAfter = Math.atan2(diffYAfter, diffXAfter) / Math.PI * 180;

    // 旋转的角度
    if (ROTATE_ENABLED) {
        this.rotate = currentGraph.rotate + angleAfter - angleBefore;
    }

    const lineA = Math.sqrt(Math.pow((this.centerX - px), 2) + Math.pow((this.centerY - py), 2));
    const lineB = Math.sqrt(Math.pow((this.centerX - x), 2) + Math.pow((this.centerY - y), 2));
    if (this.type === 'image') {
        let resize_rito = lineB / lineA;
        let new_w = currentGraph.w * resize_rito;
        let new_h = currentGraph.h * resize_rito;

        if (currentGraph.w < currentGraph.h && new_w < this.MIN_WIDTH) {
            new_w = this.MIN_WIDTH;
            new_h = this.MIN_WIDTH * currentGraph.h / currentGraph.w;
        } else if (currentGraph.h < currentGraph.w && new_h < this.MIN_WIDTH) {
            new_h = this.MIN_WIDTH;
            new_w = this.MIN_WIDTH * currentGraph.w / currentGraph.h;
        }

        this.w = new_w;
        this.h = new_h;
        this.x = currentGraph.x - (new_w - currentGraph.w) / 2;
        this.y = currentGraph.y - (new_h - currentGraph.h) / 2;

    } else if (this.type === 'text') {
        const fontSize = currentGraph.fontSize * ((lineB - lineA) / lineA + 1);
        this.fontSize = fontSize <= this.MIN_FONTSIZE ? this.MIN_FONTSIZE : fontSize;

        // 旋转位移后重新计算坐标
        this.ctx.setFontSize(this.fontSize);
        const textWidth = this.ctx.measureText(this.text).width;
        const textHeight = this.fontSize + 10;
        this.w = textWidth;
        this.h = textHeight;
        // 字体区域中心点不变,左上角位移
        this.x = this.centerX - textWidth / 2;
        this.y = this.centerY - textHeight / 2;
    }
},
toPx(rpx) {
    return rpx * this.factor;
},

};
Component({
/**
* 组件的属性列表
*/
properties: {
graph: {
type: Object,
value: {},
observer: 'onGraphChange',
},
bgColor: {
type: String,
value: '',
},
bgImage: {
type: String,
value: '',
},
bgSourceId: {
type: String,
value: '',
},
width: {
type: Number,
value: 750,
},
height: {
type: Number,
value: 750,
},
alpha:{
type:Number,
value:0.6,
}
},

/**
 * 组件的初始数据
 */
data: {},

attached() {
    const sysInfo = wx.getSystemInfoSync();
    const screenWidth = sysInfo.screenWidth;
    this.factor = screenWidth / 750;

    if (typeof this.drawArr === 'undefined') {
        this.drawArr = [];
    }
    this.ctx = wx.createCanvasContext('canvas-label', this);
    this.draw();
},

/**
 * 组件的方法列表
 */
methods: {
    toPx(rpx) {
        return rpx * this.factor;
    },
    onGraphChange(n, o) {
        if (JSON.stringify(n) === '{}') return;
        this.drawArr.push(new dragGraph(Object.assign({
            x: 30,
            y: 30,
        }, n), this.ctx, this.factor));
        this.draw();
    },
    initByArr(newArr) {
        this.drawArr = [];
        // 循环插入 drawArr
        newArr.forEach((item, index) => {
            switch (item.type) {
                case 'bgColor':
                    this.data.bgImage = '';
                    this.data.bgSourceId = '';
                    this.data.bgColor = item.color;
                    break;
                case 'bgImage':
                    this.data.bgColor = '';
                    this.data.bgImage = item.url;
                    if (item.sourceId) {
                        this.data.bgSourceId = item.sourceId;
                    }
                    break;
                case 'image':
					console.log(item.sourceId);
                case 'text':
                    if (index === newArr.length - 1) {
                        item.selected = true;
                    } else {
                        item.selected = false;
                    }
                    this.drawArr.push(new dragGraph(item, this.ctx, this.factor));
                    break;
            }

        });
        this.draw();
    },
    draw() {
        if (this.data.bgImage !== '') {
            this.ctx.drawImage(this.data.bgImage, 0, 0, this.toPx(this.data.width), this.toPx(this.data.height));
        }
        if (this.data.bgColor !== '') {
            this.ctx.save();
            this.ctx.setFillStyle(this.data.bgColor);
            this.ctx.fillRect(0, 0, this.toPx(this.data.width), this.toPx(this.data.height));
            this.ctx.restore();
        }
		console.log("draw");
        this.drawArr.forEach((item) => {
            item.paint();
        });
        return new Promise((resolve) => {
            this.ctx.draw(false, () => {
                resolve();
            });
        });
    },
    start(e) {
        const {x, y} = e.touches[0];
        this.tempGraphArr = [];
        let lastDelIndex = null; // 记录最后一个需要删除的索引
        //****************新加代码**********************
		let change_pos_idx = -1;
        //****************新加代码结束**********************
        this.drawArr && this.drawArr.forEach((item, index) => {
            const action = item.isInGraph(x, y);
            if (action) {
                item.action = action;
                this.tempGraphArr.push(item);
                // 保存点击时的坐标
                this.currentTouch = {x, y};
                if (action === 'del') {
					console.log(item.sourceId);//xmcase

					const myEventDetail = {delid:item.sourceId} // detail对象,提供给事件监听函数
					const myEventOption = {} // 触发事件的选项
					this.triggerEvent('myevent', myEventDetail, myEventOption);

                    lastDelIndex = index;// 标记需要删除的元素
                }

                //****************新加代码********************** 
				//如果点的不是删除,同时点的是其它层,则要把这层置顶
				if(action !== 'del' && index <= this.drawArr.length-1){
					change_pos_idx =index;
				}
                //*************新加代码结束***************** 

            } else {
                item.action = false;
                item.selected = false;
            }
        });

        //****************新加代码**********************
		if (change_pos_idx > -1 && change_pos_idx != this.drawArr.length - 1 && lastDelIndex == null) {
			console.log(change_pos_idx);
			this.drawArr = change_pos(this.drawArr, change_pos_idx, this.drawArr.length - 1);
			this.ctx.clearRect(0, 0, this.toPx(this.data.width), this.toPx(this.data.height));
		}
        //*************新加代码结束*****************

        // 保存点击时元素的信息
        if (this.tempGraphArr.length > 0) {
            for (let i = 0; i < this.tempGraphArr.length; i++) {
                let lastIndex = this.tempGraphArr.length - 1;
                // 对最后一个元素做操作
                if (i === lastIndex) {
                    // 未选中的元素,不执行删除和缩放操作
                    if (lastDelIndex !== null && this.tempGraphArr[i].selected) {
                        if (this.drawArr[lastDelIndex].action === 'del') {
                            this.drawArr.splice(lastDelIndex, 1);
							lastDelIndex = null;
                            this.ctx.clearRect(0, 0, this.toPx(this.data.width), this.toPx(this.data.height));
                        }
                    } else {
                        this.tempGraphArr[lastIndex].selected = true;
                        this.currentGraph = Object.assign({}, this.tempGraphArr[lastIndex]);
                    }
                } else {
                    // 不是最后一个元素,不需要选中,也不记录状态
                    this.tempGraphArr[i].action = false;
                    this.tempGraphArr[i].selected = false;
                }
            }
        }


		
        this.draw();
    },
    move(e) {
        const {x, y} = e.touches[0];
        if (this.tempGraphArr && this.tempGraphArr.length > 0) {
            const currentGraph = this.tempGraphArr[this.tempGraphArr.length - 1];
            if (currentGraph.action === 'move') {
                currentGraph.centerX = this.currentGraph.centerX + (x - this.currentTouch.x);
                currentGraph.centerY = this.currentGraph.centerY + (y - this.currentTouch.y);
                // 使用中心点坐标计算位移,不使用 x,y 坐标,因为会受旋转影响。
                if (currentGraph.type !== 'text') {
                    currentGraph.x = currentGraph.centerX - this.currentGraph.w / 2;
                    currentGraph.y = currentGraph.centerY - this.currentGraph.h / 2;
                }
            } else if (currentGraph.action === 'transform') {
                currentGraph.transform(this.currentTouch.x, this.currentTouch.y, x, y, this.currentGraph);
            }
            // 更新4个坐标点(相对于画布的坐标系)
            currentGraph._rotateSquare();

            this.draw();
        }
    },
    end(e) {
        this.tempGraphArr = [];
    },
    export() {
        return new Promise((resolve, reject) => {
            this.drawArr = this.drawArr.map((item) => {
                item.selected = false;
                return item;
            });
            this.draw().then(() => {
                wx.canvasToTempFilePath({
                    canvasId: 'canvas-label',
                    success: (res) => {
                        resolve(res.tempFilePath);
                    },
                    fail: (e) => {
                        reject(e);
                    },
                }, this);
            });
        })
    },
    exportJson() {
        return new Promise((resolve, reject) => {
            let exportArr = this.drawArr.map((item) => {
                item.selected = false;
                switch (item.type) {
                    case 'image':
                        return {
                            type: 'image',
                            url: item.fileUrl,
                            y: item.y,
                            x: item.x,
                            w: item.w,
                            h: item.h,
                            rotate: item.rotate,
                            sourceId: item.sourceId,
                        };
                        break;
                    case 'text':
                        return {
                            type: 'text',
                            text: item.text,
                            color: item.color,
                            fontSize: item.fontSize,
                            y: item.y,
                            x: item.x,
                            w: item.w,
                            h: item.h,
                            rotate: item.rotate,
                        };
                        break;
                }
            });
            if (this.data.bgImage) {
                let tmp_img_config = {
                    type: 'bgImage',
                    url: this.data.bgImage,
                };
                if (this.data.bgSourceId) {
                    tmp_img_config['sourceId'] = this.data.bgSourceId;
                }
                exportArr.unshift(tmp_img_config);
            } else if (this.data.bgColor) {
                exportArr.unshift({
                    type: 'bgColor',
                    color: this.data.bgColor
                });
            }

            resolve(exportArr);
        })
    },
    changColor(color) {
        const selected = this.drawArr.filter((item) => item.selected);
        if (selected.length > 0) {
            selected[0].color = color;
        }
        this.draw();
    },
    changeBgColor(color) {
        this.data.bgImage = '';
        this.data.bgColor = color;
        this.draw();
    },
    changeBgImage(newBgImg) {
        this.data.bgColor = '';
        if (typeof newBgImg == 'string') {
            this.data.bgSourceId = '';
            this.data.bgImage = newBgImg;
        } else {
            this.data.bgSourceId = newBgImg.sourceId;
            this.data.bgImage = newBgImg.url;
        }
        this.draw();
    },
    clearCanvas() {
        this.ctx.clearRect(0, 0, this.toPx(this.data.width), this.toPx(this.data.height));
        this.ctx.draw();
        this.drawArr = [];
        this.data.bgColor = '';
        this.data.bgSourceId = '';
        this.data.bgImage = '';
    }
}

});

@zlzym
Copy link

zlzym commented Sep 24, 2019

dist目录呢

@charrysong
Copy link

charrysong commented Jul 29, 2021

能用画面外面的按钮让选中的图层置底吗?因为大图跟小图切换如果在里面是很难切换的 @jasondu
已经通过getrgap()循环再setrgap()设置。可以实现。
setDown:function(){ var that=this; var crentgap=CanvasDrag.getrgap(); var newdrawArr=[]; var selectedindex=crentgap.length-1; var lastArr=[]; var lastdrawArr=[]; if(crentgap.length>1){ crentgap.forEach((item, index) => { console.log(item.selected); if(item.selected){ console.log(selectedindex) lastArr.push(item); } else{ newdrawArr.push(item); } }); if(lastArr.length==0){ newdrawArr=[]; crentgap.forEach((item, index) => { if(index<selectedindex){ console.log(selectedindex) newdrawArr.push(item); } }); lastArr.push(crentgap[selectedindex]); lastdrawArr=lastArr.concat(newdrawArr); } else{ lastdrawArr=lastArr.concat(newdrawArr); } CanvasDrag.setrgap(lastdrawArr); } },

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

7 participants