绘画板 10——多选元素

本贴最后更新于 3006 天前,其中的信息可能已经水流花落

github 地址: https://github.com/wangyuheng/painter

DEMO 地址: http://painter.crick.wang/

多选元素

原理

在选择按钮状态下,可以绘制一个曲线矩形,遍历所有元素判断,如果当前元素在矩形的坐标范围内,则元素被选中。

矩形四个点的坐标范围为 x,x+width,y,y+height

变更 element click 事件

选择元素时,需要触发元素选中事件。如果触发 click,会增加额外判断,并延时。所以单独抽离 pick 事件,将 click 中的事件处理指向 pick

_ele.on("click", function() {
	if (GlobalStatus.isPreFilled()) {
		if ($("#fill_color").hasClass("active")) {
			_ele.fill(GlobalStatus.getFillColor());
			_ele.style("fill-opacity", GlobalStatus.getFillOpacity());
		} else {
			_ele.style("stroke", GlobalStatus.getFontColor());
		}

	} else if (GlobalStatus.isPicked()) {
		_ele.fire("pick");
	} else if (GlobalStatus.isRecycle()) {
		_ele.remove();
	}
});
_ele.on("pick", function() {
	if (_ele.attr("picked")) {
		_ele.attr("picked", null);
		_ele.handleBorder && _ele.handleBorder.hideShade(_ele);
		GlobalStatus.removePicked(_ele);
	} else {
		_ele.attr("picked", true);
		_ele.handleBorder = _ele.handleBorder || new HandleBorder(svgDoc);
		_ele.handleBorder.showShade(_ele);
		GlobalStatus.pushPicked(_ele);

	}
});

新增 draw.tool.pick.js

新增 DrawTool.Pick,操作与 Rect 类似,唯一的区别在于 mouseup 时判断元素是否在绘制的虚线矩形范围内,并对应触发 pick 事件。

(function() {

	var parent = null;
	var drawing = false;
	var element = null;
	var startPoint = null;

	function mousedown(event) {
		console.log('pick mousedown');
		drawing = true;
		startPoint = svgDoc.transformPoint(event);
		element = parent.rect(0, 0).fill(GlobalStatus.getFillColor()).style({
			"fill-opacity": GlobalStatus.getFillOpacity(),
			"stroke-dasharray": "13 10"
		}).stroke({
			width: "1",
			color: "grey"
		});
		return false;
	}

	function mousemove(event) {
		console.log('pick mousemove');
		if (drawing) {
			var svgPoint = svgDoc.transformPoint(event);
			var x = svgPoint.x;
			var y = svgPoint.y;

			var newWidth = x - startPoint.x;
			var newHeight = y - startPoint.y;
			var startX = startPoint.x;
			var startY = startPoint.y;
			if (newWidth < 0) {
				startX += newWidth;
			}

			if (newHeight < 0) {
				startY += newHeight;
			}
			newWidth = Math.abs(newWidth);
			newHeight = Math.abs(newHeight);
			element.x(startX).y(startY).width(newWidth).height(newHeight);
		}
		return false;
	};

	function mouseup(event) {
		console.log('pick mouseup ' + element);
		drawing = false;
		if (element.attr("width") > 0) {
			var sx = element.x();
			var ex = element.x() + element.width();
			var sy = element.y();
			var ey = element.y() + element.height();
			$(GlobalStatus.getAllElements()).each(function() {
				console.log(this.x(), this.y(), sx < this.x() && this.x() < ex && sy < this.y() && this.y() < ey);
				if (sx < this.x() && this.x() < ex && sy < this.y() && this.y() < ey) {
					if (!this.attr("picked")) {
						this.fire("pick");
					}
				} else if (this.attr("picked")) {
					this.fire("pick");
				}
			})
		}
		parent.removeElement(element);
		return false;
	}

	var listener = {
		mousedown: mousedown,
		mousemove: mousemove,
		mouseup: mouseup,
	};


	var Pick = function(parentEle) {
		parent = parentEle;
		console.log(parent);
		svgDoc = parent.doc();
		DrawTool.init(svgDoc, listener);
		this.stop = function() {
			DrawTool.stop(svgDoc, listener);
		};
	};

	this.DrawTool.Pick = Pick;

})();

首页监听选择按钮

在首页监听选择按钮,被选中时,创建 DrawTool.Pick 对象

$("#tool_pick").on("click", function() {
	resetCurrentDrawTool();
	currentDrawTool = new DrawTool.Pick(svgDoc);
});

弊端

有一个不足的地方,元素必须全在范围内,才能被选中。如果被选中部分,则无法选中。没想到好的解决方案。

bug 修复

picked 状态混乱

click 和 mouseup 互相冲突,导致选中状态丢失。

将 pick 拆分为 pick 和 unPick 两个事件,分别处理选中状态,而不进行判断。将判断交给上层调用方法

 _ele.on("click", function() {
	  console.log("click");
	  if (GlobalStatus.isPreFilled()) {
		  if ($("#fill_color").hasClass("active")) {
			  _ele.fill(GlobalStatus.getFillColor());
			  _ele.style("fill-opacity", GlobalStatus.getFillOpacity());
		  } else {
			  _ele.style("stroke", GlobalStatus.getFontColor());
		  }

	  } else if (GlobalStatus.isPicked()) {
		  if (_ele.attr("picked")) {
			  _ele.fire("unPick");
		  } else {
			  _ele.fire("pick");
		  }

	  } else if (GlobalStatus.isRecycle()) {
		  _ele.remove();
	  }
  });
  _ele.on("pick", function() {
	  console.log("pick");
	  _ele.attr("picked", true);
	  _ele.handleBorder = _ele.handleBorder || new HandleBorder(svgDoc);
	  _ele.handleBorder.showShade(_ele);
	  GlobalStatus.pushPicked(_ele);
  });
  _ele.on("unPick", function() {
	  console.log("unPick");
	  _ele.attr("picked", null);
	  _ele.handleBorder && _ele.handleBorder.hideShade(_ele);
	  GlobalStatus.removePicked(_ele);

  });

Pick 中

function mouseup(event) {
    console.log('pick mouseup ' + element);
    if (drawing) {
        drawing = false;
        if (element && element.attr("width") > 20) {
            var sx = element.x();
            var ex = element.x() + element.width();
            var sy = element.y();
            var ey = element.y() + element.height();
            $(GlobalStatus.getAllElements()).each(function() {
                console.log(this.x(), this.y(), sx < this.x() && this.x() < ex && sy < this.y() && this.y() < ey);
                if (sx < this.x() && this.x() < ex && sy < this.y() && this.y() < ey) {
                    if (!this.attr("picked")) {
                        this.fire("pick");
                    }
                } else if (this.attr("picked")) {
                    this.fire("unPick");
                }
            })
        }
        element && element.remove();
    }
    return false;
}

Pick 拥有背景色

修改 Pick 的 mousedown 方法,独立设置 style,不关联颜色选择器

function mousedown(event) {
    console.log('pick mousedown');
    if (!drawing) {
        drawing = true;
        startPoint = svgDoc.transformPoint(event);
        element = parent.rect(0, 0).style({
            "fill-opacity": "0.0",
            "stroke-dasharray": "10"
        }).stroke({
            width: "1",
            color: "grey"
        });
    }
    return false;
}

mousedown 状态鼠标移出画板范围,松开鼠标,再次回到画板范围内,导致状态丢失

再回到画板时,鼠标已经移开,但是并未执行 mouseup 方法,因为时间的监听范围为画板内。所以 mouseover 事件继续执行,导致多生成了一个 element。为了避免此问题,在 mouse 事件中增加 drawing 状态判断,以 Rect 为例

(function() {

	var parent = null;
	var drawing = false;
	var element = null;
	var startPoint = null;

	function mousedown(event) {
		console.log('rect mousedown');
		if (!drawing) {
			drawing = true;
			startPoint = svgDoc.transformPoint(event);
			element = parent.rect(0, 0).fill(GlobalStatus.getFillColor()).style("fill-opacity", GlobalStatus.getFillOpacity()).stroke({
				width: GlobalStatus.getLineSize(),
				color: GlobalStatus.getFontColor()
			});
		}
		return false;
	}

	function mousemove(event) {
		console.log('rect mousemove');
		if (drawing) {
			var svgPoint = svgDoc.transformPoint(event);
			var x = svgPoint.x;
			var y = svgPoint.y;

			var newWidth = x - startPoint.x;
			var newHeight = y - startPoint.y;
			var startX = startPoint.x;
			var startY = startPoint.y;
			if (newWidth < 0) {
				startX += newWidth;
			}

			if (newHeight < 0) {
				startY += newHeight;
			}
			newWidth = Math.abs(newWidth);
			newHeight = Math.abs(newHeight);
			element.x(startX).y(startY).width(newWidth).height(newHeight);
		}
		return false;
	};

	function mouseup(event) {
		console.log('rect mouseup ' + element);
		if (drawing) {
			drawing = false;
			if (element.attr("width") > 0) {
				element.pickable();
			} else {
				parent.removeElement(element);
			}
		}
		return false;
	}

	var listener = {
		mousedown: mousedown,
		mousemove: mousemove,
		mouseup: mouseup,
	};


	var Rect = function(parentEle) {
		parent = parentEle;
		console.log(parent);
		svgDoc = parent.doc();
		DrawTool.init(svgDoc, listener);
		this.stop = function() {
			DrawTool.stop(svgDoc, listener);
		};
	};

	this.DrawTool.Rect = Rect;

})();

所有 DrawTool 类方法都要增加此

bug 修复

选择其他 DrawTool 后,选中状态不丢失。

在 GlobalStatus 增加清除所有选中状态的方法

unPickAll() {
	$(GlobalStatus.getPickeds()).each(function() {
		this.fire("unPick");
	});
	return this;
}

在 index 的 resetCurrentDrawTool 方法中调用执行

function resetCurrentDrawTool() {
	currentDrawTool && currentDrawTool.stop();
	GlobalStatus.unPickAll();
	$("#svgPanel").css("cursor", "default");
}

同时给右键的取消按钮增加此方法调用

label: ' 取消',
action: function () {
	GlobalStatus.unPickAll();
	return false;
}
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    407 引用 • 3578 回帖 • 1 关注
  • SVG
    24 引用 • 73 回帖
  • Painter
    14 引用 • 31 回帖
  • 多选
    1 引用 • 2 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • relyn

    前端真好玩

  • 其他回帖
  • 714593351

    ]ۣۣۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖ[]ۣۣۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖۖ