+ React.useEffect(() => {
+ const canvas = select('.canvas');
+ const bbox = canvas.select('.background');
+ const start = { x: 0, y: 0 };
+ const onStart = function (e) {
+ start.x = e.x;
+ start.y = e.y;
+ };
+ const onDrag = function (e) {
+ const bounds = bbox.node().getBoundingClientRect();
+ const distance = Math.max(Math.abs(e.x - start.x), Math.abs(e.y - start.y));
+ if (distance > 5) {
+ setDragging({
+ icon: this.dataset['icon'],
+ x: (e.x - bounds.x) / bounds.width,
+ y: (e.y - bounds.y) / bounds.height,
+ });
+ } else {
+ setDragging(null);
+ }
+ };
+ const onEnd = function (e) {
+ const bounds = bbox.node().getBoundingClientRect();
+ setDragging(null);
+ const distance = Math.max(Math.abs(e.x - start.x), Math.abs(e.y - start.y));
+ if (distance > 5) {
+ addPin({
+ icon: this.dataset['icon'],
+ x: (e.x - bounds.x) / bounds.width,
+ y: (e.y - bounds.y) / bounds.height,
+ });
+ if (this.classList.contains('map-pin')) {
+ let id = 0;
+ this.classList.forEach(name => {
+ if (name.startsWith('map-pin-')) {
+ id = parseInt(name.substr(8), 10);
+ }
+ });
+ removePin({ id });
+ }
+ }
+ };
+ const selection = canvas.selectAll('.toggle-icon');
+ const draggable = drag()
+ .container(bbox)
+ .clickDistance(5)
+ .on('start', onStart)
+ .on('drag', onDrag)
+ .on('end', onEnd);
+ selection.call(draggable);
+ return () => {
+ selection.on('.drag', null);
+ };
+ }, [pins, removePin]);
+