arcgisjs学习笔记2实现仿百度的拖拽画圆

author author     2022-08-02     172

关键词:

一、前言

      吐槽一下,百度在国内除了百度地图是良心产品外,其他的真的不敢恭维。在上一篇笔记里,我已经实现了自定义的地图测量模块。在百度地图里面(其他地图)都有一个周边搜索的功能,拖拽画一个圆,然后以圆半径进行搜索(也就是缓冲区╮(╯_╰)╭)。

技术分享

 

这次的目标,就是要山寨这个拖拽画圆的功能,我先放一个效果图。

技术分享

 

二、开始山寨

我们先想一想要实现这个功能需要哪些步骤。

  1. 拖拽
  2. 画圆
  3. 通知拖拽结束

2.1 实现拖拽

    关于拖拽,有graphicslayer的拖拽事件和map的拖拽事件,如何选择呢?先来看一看官方文档。

技术分享

graphicslayer 的鼠标事件

技术分享

map的鼠标事件

      在graphic的鼠标事件里面,鼠标事件触发的条件是鼠标必须在一个graphic上(红色标记处),但是graphicslayer的mouse-drag事件好像并不要这个条件,而且事件说明和map的一样。我们在仔细看一下文档,Arcgis文档在这个细节处理上特别值得学习。graphicslayer和map的鼠标事件文档中,开头都是mouse-down(mouse button is pressed down),结尾都是mouse-up(mouse button is released)。现在大家都发现了吧,两者的drag事件都是和mouse-down、mouse-up有关联的。首先,按下鼠标(mouse-down)是触发drag 的前提条件。然后,松开鼠标(mouse-up)是drag事件结束的标识。也就是说,如果要触发drag事件,就一定会触发mouse-down和mouse-up事件,所以graphicslayer的drag事件也需要鼠标在graphic上才能触发。

     解释的不错,我选择map!下面先上两段代码来说一下为什么要选择map的drag事件原因。

map的鼠标事件,添加了一个graphicslayer和一个graphic

require([
                "dojo/dom", "dojo/on",
                "esri/map","esri/layers/GraphicsLayer", "esri/geometry/Point", "esri/symbols/SimpleMarkerSymbol",
                "esri/symbols/SimpleLineSymbol", "esri/graphic", "esri/Color",
                "dojo/domReady!"],
            function (dom, on, Map, GraphicsLayer,Point,
                      SimpleMarkerSymbol, SimpleLineSymbol, Graphic, Color) {
                var map = new Map("map", {
                    center: [103, 24.5],
                    zoom: 9,
                    basemap: "osm"
                });
                var graphicsLayer=new GraphicsLayer();
                map.addLayer(graphicsLayer);
                map.on("load", function () {
                    var sms = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 20,
                            new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
                                    new Color([255, 0, 0]), 1),
                            new Color([0, 255, 0, 0.25]));
                    var point = new Point(103, 24.5);
                    var graphic = new Graphic(point, sms);
                    map.graphics.add(graphic);
                    graphicsLayer.add(graphic);
                });

                map.on("mouse-down", function (evt) {
                    console.log("map:mouse-down");
                });

                map.on("mouse-drag", function (evt) {
                    console.log("map:mouse-drag");
                });

                map.on("mouse-up", function (evt) {
                    console.log("map:mouse-up");
                });
            });

当在map上进行拖拽时,控制台的输出如下:

技术分享

当把鼠标移动到graphic上进行拖拽时,控制台输出如下:

技术分享

它也触发了地图的拖拽事件。

接着在看一看graphicslayer的鼠标事件,我添加了一个graphicslayer和一个graphic。

require([
                "dojo/on",
                "esri/map", "esri/layers/GraphicsLayer", "esri/geometry/Point", "esri/symbols/SimpleMarkerSymbol",
                "esri/symbols/SimpleLineSymbol", "esri/graphic", "esri/Color",
                "dojo/domReady!"],
            function (on, Map, GraphicsLayer, Point,
                      SimpleMarkerSymbol, SimpleLineSymbol, Graphic, Color) {
                var map = new Map("map", {
                    center: [102, 24.5],
                    zoom: 9,
                    basemap: "osm"
                });
                var graphicsLayer=new GraphicsLayer();
var graphic; 
                map.addLayer(graphicsLayer);
                map.on("load", function () {
                    var sms = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 20,
                            new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID,
                                    new Color([255, 0, 0]), 1),
                            new Color([0, 255, 0, 0.25]));
                    var point = new Point(102, 24.5);
                    graphic = new Graphic(point, sms);
                    graphicsLayer.add(graphic);
                    console.log(map.graphics)
                });
                graphicsLayer.on("mouse-down", function (evt) {
                    console.log("graphicslayer:mouse-down");
                });

                graphicsLayer.on("mouse-drag", function (evt) {
                    console.log("graphicslayer:mouse-drag");
                });

                graphicsLayer.on("mouse-up", function (evt) {
                    console.log("graphicslayer:mouse-up");
                });

            });

      当在map上拖拽时候,这时候给人的感觉应该是,graphicslayer也在地图上,也应该会触发graphicslayer的拖拽事件,然而并没有,这时候控制台的输出为:

技术分享

     当把鼠标移动到graphic上进行拖拽时,控制台输出如下:

技术分享

这时终于触发了graphicslayer的拖拽事件。

到现在为止,感觉好像二者区别不大。但是在进行拖拽时,移动的是地图,我们要实现的效果是移动graphic,这时就要用到如下方法:

技术分享

我们先来实现在graphicslayer上移动graphic。

graphicsLayer.on("mouse-down", function (evt) {
      console.log("graphicslayer:mouse-down");
      map.disableMapNavigation();
     });

 graphicsLayer.on("mouse-drag", function (evt) {
       console.log("graphicslayer:mouse-drag");
       graphic.setGeometry(evt.mapPoint);
     });

graphicsLayer.on("mouse-up", function (evt) {
       console.log("graphicslayer:mouse-up");
       map.enableMapNavigation();
     });

我们把graphic移动到昆明市,看看控制台的输出:

技术分享 技术分享技术分享

这时在拖拽事件里移动了graphic,而且事件也按预期的顺序发生了。但是!但是!但是!这是鼠标一直在graphic上的时候才能触发的事件,当我们飞快的移动鼠标,使鼠标不在graphic上,这时就会有奇怪的行为发生了。

还是把graphic移动到昆明市(以很快的速度),看看控制台的输出:

技术分享技术分享技术分享

当鼠标移动到昆明市的时候,松开鼠标,并没有触发mous-up事件。现在在吧鼠标移到graphic上,你会发现不用点击鼠标graphic也会随着鼠标一起移动,要停止的话只有再次点击鼠标并松开,这时控制台输出如下:

技术分享

所以如果选用graphiclayer的drag事件来实现拖拽按钮的话,用户体验会很糟糕,所以graphicslayer的drag事件不能用!

接下来实现map的drag事件,删除原来map的mouse-donw 事件,替换成graphicslayerdmouse-down。接着在graphic上加了个

isMouseDown属性,判断是否要拖拽这个graphic。

graphicsLayer.on("mouse-down", function (evt) {
     console.log("graphicslayer:mouse-down");
     graphic.isMouseDown=true;
     map.disableMapNavigation();
   });

map.on("mouse-drag", function (evt) {
     console.log("map:mouse-drag");
     if( graphic.isMouseDown){
     graphic.setGeometry(evt.mapPoint);
     }
  });

map.on("mouse-up", function (evt) {
      console.log("map:mouse-up");
      map.enableMapNavigation();
      graphic.isMouseDown=false;
   });

这次就能很好的解决在graphicslayer上遇到的问题。

2.2画圆

解决了拖拽的问题,接下来就可以实现拖拽画圆了。我们传入中心点绘制制初始化圆,默认半径为500米,

startDrawCircle: function (centerPoint) {
                this._unregistMapEvents();
                this.centerPoint = centerPoint;
                this.circle = this._createCircle(centerPoint, 500);
                var dragPoint = this._createDragBtnPoint(this.circle, centerPoint);

                this.circleGraphic = new Graphic(this.circle, this.defaults.fillSymbol);
                this.labelGraphic = new Graphic(dragPoint, this._createDistanceSymbol(500));
                var dragGraphic = new Graphic(dragPoint, this.defaults.dragButtonSymbol);

                this._measureLayer.add(this.circleGraphic);
                this._measureLayer.add(dragGraphic);
                this._measureLayer.add(this.labelGraphic);
                this._initialMapEvents();
            },

第一步我们先取消上一次的画圆注册的map鼠标事件,第二步添加初始化圆,第三添加拖拽按钮和半径文描述。在计算拖拽按钮的为止时,可以用圆的extent来进行计算。

_createDragBtnPoint: function (geometry, center) {
                var extent = geometry.getExtent();
                var xmax = extent.xmax;
                returnnew Point([xmax, center.y], center.spatialReference)
            },

好了,现在所有的准备工作已经就绪,在结合前面的graphic拖拽,就可以轻松愉快的完成拖拽画圆了。

2.3通知拖拽结束

当每一次拖拽结束是,发出一次通知告诉用户绘制结束是很有必要的。这次就借助map的drag-end事件来通知用户

map.on("mouse-drag-end", lang.hitch(this, function (evt) {
  if (this.dragGraphic && this.dragGraphic.isMouseDown) {
   this.emit("drag-end", {circle: this.circle});
   this.dragGraphic.isMouseDown = false;
   this.defaults.map.enableMapNavigation();
   this.defaults.map.setMapCursor("default");
   }
})

通过 this.emit("drag-end", {circle: this.circle}); 我们就可以向外部发出拖拽结束的通知。

2.4 源码

/**
 * Created by Extra 
 * Description:实现拖拽绘制圆,仿百度缓冲区搜索样式
 * version: 1.0.0
 */
define("dextra/dijit/DrawDragCircle", [
        "require",
        "dojo/dom",
        "dojo/query",
        "dojo/_base/declare",
        "dojo/_base/lang",
        "dojo/Evented",
        "dojo/on",
        "esri/graphic",
        "esri/layers/GraphicsLayer",
        "esri/Color",
        "esri/symbols/Font",
        "esri/geometry/Point",
        "esri/geometry/Circle",
        "esri/geometry/Polyline",
        "esri/symbols/SimpleMarkerSymbol",
        "esri/symbols/PictureMarkerSymbol",
        "esri/symbols/SimpleLineSymbol",
        "esri/symbols/SimpleFillSymbol",
        "esri/symbols/TextSymbol",
        "esri/geometry/geometryEngine",
    ],
    function (require, dom, query, declare, lang, Evented, on,
              Graphic, GraphicsLayer,
              Color, Font, Point, Circle, Polyline, MarkerSymbol, PictureMarkerSymbol, LineSymbol, FillSymbol, TextSymbol, geometryEngine) {
        return declare(Evented, {
            declaredClass: "dextra.dijit.DrawDragCircle",
            defaults: {
                map: null,
                maxRadius: 5000,
                markerSymbol: new MarkerSymbol(MarkerSymbol.STYLE_SQUARE, 20,
                    new LineSymbol(LineSymbol.STYLE_SOLID,
                        new Color("#DC143C"), 2),
                    new Color("#FFA500")),
                dragButtonSymbol: new PictureMarkerSymbol({
                    "url": require.toUrl("./images/dragButton.png"),
                    "height": 21,
                    "width": 33
                }),
                lineSymbol: new LineSymbol(
                    LineSymbol.STYLE_SOLID,
                    new Color("#FFA500"), 2),
                fillSymbol: new FillSymbol(FillSymbol.STYLE_SOLID,
                    new LineSymbol(LineSymbol.STYLE_SOLID,
                        new Color([0, 155, 255, 0.55]), 2), new Color([0, 155, 255, 0.55])),
            },
            circleGraphic: null,
            circle: null,
            labelGraphic: null,
            dragGraphic: null,
            _measureLayer: null,
            _mapEvents: [],

            constructor: function (options) {
                declare.safeMixin(this.defaults, options);
                this._measureLayer = new GraphicsLayer();
                this.defaults.map.addLayer(this._measureLayer);
                this._initialMeasureLayer();

            },

            //初始化测量图层事件
            _initialMeasureLayer: function () {
                //开始拖拽绘制圆this._measureLayer.on("mouse-down", lang.hitch(this, function (evt) {
                    var graphic = evt.graphic;
                    if (graphic.symbol.type == "picturemarkersymbol") {
                        this.dragGraphic = graphic;
                        this.dragGraphic.isMouseDown = true;
                        this.defaults.map.disableMapNavigation();
                        graphic.getDojoShape().moveToFront();
                        this.defaults.map.setMapCursor("pointer");
                    }
                }));

                //提示可以拖拽this._measureLayer.on("mouse-over", lang.hitch(this, function (evt) {
                    var graphic = evt.graphic;
                    if (graphic.symbol.type == "picturemarkersymbol") {
                        this.defaults.map.setMapCursor("pointer");
                    }
                }));

               //恢复鼠标状态this._measureLayer.on("mouse-out", lang.hitch(this, function (evt) {
                    this.defaults.map.setMapCursor("default");
                }));
            },

            _initialMapEvents: function () {
                this._mapEvents = [];
                //拖拽绘制圆this._mapEvents.push(this.defaults.map.on("mouse-drag", lang.hitch(this, function (evt) {
                    if (this.dragGraphic != null && this.dragGraphic.isMouseDown) {
                        var dragGraphic = this.dragGraphic;
                        var dragPoint = evt.mapPoint;
                        if (this.centerPoint.y != dragPoint.y) {
                            dragPoint.setY(this.centerPoint.y);
                        }
                        var radius = this._calDistance(this.centerPoint, dragPoint);
                        if (radius <= this.defaults.maxRadius) {
                            this._measureLayer.remove(this.circleGraphic);
                            this.circle = this._createCircle(this.centerPoint, radius);
                            this.circleGraphic = new Graphic(this.circle, this.defaults.fillSymbol);
                            dragGraphic.setGeometry(dragPoint);

                            this.labelGraphic.setGeometry(dragPoint).setSymbol(this._createDistanceSymbol(radius))
                            this._measureLayer.add(this.circleGraphic);
                            this.circleGraphic.getDojoShape().moveToBack();
                            dragGraphic.getDojoShape().moveToFront();
                        }
                    }
                })));

                //触发"mouse-drag-end,通知拖拽结束this._mapEvents.push(this.defaults.map.on("mouse-drag-end", lang.hitch(this, function (evt) {
                    if (this.dragGraphic && this.dragGraphic.isMouseDown) {
                        this.emit("drag-end", {circle: this.circle});

                        this.dragGraphic.isMouseDown = false;
                        this.defaults.map.enableMapNavigation();
                        this.defaults.map.setMapCursor("default");
                    }
                })));
            },

            //取消上一次注册的map鼠标事件
            _unregistMapEvents: function () {
                for (var i = 0; i < this._mapEvents; i++) {
                    if (this._mapEvents[i]) {
                        this._mapEvents[i].remove();
                    }
                }
                this._mapEvents=[];
            },

            startDrawCircle: function (centerPoint) {
                this._unregistMapEvents();
                this.centerPoint = centerPoint;
                this.circle = this._createCircle(centerPoint, 500);
                var dragPoint = this._createDragBtnPoint(this.circle, centerPoint);

                this.circleGraphic = new Graphic(this.circle, this.defaults.fillSymbol);
                this.labelGraphic = new Graphic(dragPoint, this._createDistanceSymbol(500));
                var dragGraphic = new Graphic(dragPoint, this.defaults.dragButtonSymbol);

                this._measureLayer.add(this.circleGraphic);
                this._measureLayer.add(dragGraphic);
                this._measureLayer.add(this.labelGraphic);
                this._initialMapEvents();
            },

            removeCircle: function () {
                this.centerPoint = null;
                this.circleGraphic = null;
                this.labelGraphic = null;
                this._measureLayer.clear();
            },

            _createCircle: function (point, distance) {
                returnnew Circle(point, {
                    "radius": distance
                });
            },

            _createDragBtnPoint: function (geometry, center) {
                var extent = geometry.getExtent();
                var xmax = extent.xmax;
                returnnew Point([xmax, center.y], center.spatialReference)
            },

            _createDistanceSymbol: function (distance) {
                distance = distance.toFixed(0) + "m";
                var fontColor = new Color("#696969");
                var holoColor = new Color("#fff");
                var font = new Font("10pt", Font.STYLE_ITALIC, Font.VARIANT_NORMAL, Font.WEIGHT_BOLD, "Courier");
                var textSymbol = new TextSymbol(distance, font, fontColor);
                textSymbol.setOffset(10, 20).setHaloColor(holoColor).setHaloSize(2);
                textSymbol.setAlign(TextSymbol.ALIGN_MIDDLE);
                return textSymbol;
            },

            _calDistance: function (point1, point2) {
                var line = new Polyline(this.defaults.map.spatialReference);
                line.addPath([point1, point2]);
                return geometryEngine.distance(point1, point2, "meters");
            },
        });
    })

arcgisjs学习笔记3实现百度风格的bubblepopup

1.开篇模仿是最好的学习,这次我们继续山寨百度,通过自定义Infowindow来实现百度风格的BubblePopup 2.准备2.1Copy模板先打开百度地图,按下f12吧BubblePopup的HTML代码和CSS代码拷贝下来,这里我无耻的把类名改了,大家不要在意细... 查看详情

arcgisjs学习笔记4实现地图联动

1.开篇     守望屁股实在太好玩了,所以最近有点懒,这次就先写个简单的来凑一下数。这次我的模仿目标是天地图的地图联动。天地的地图联动不仅地图有联动,而且鼠标也有联动,我就照着这个目标进行山... 查看详情

实现元素简单的拖拽

1.通过元素的offsetLeft,offsettop实现元素的拖拽1<!DOCTYPEhtml>2<html>34<head>5<metacharset="UTF-8">6<title></title>7<styletype="text/css">8.box{9width:100px;10height:100px 查看详情

pyqt5学习记录---监听鼠标拖拽事件实现mac上百度云盘拖拽效果(代码片段)

背景在Mac版的百度云盘上有这样一个效果,拖拽一个文件过来,将会显示边框蓝色非常醒目的提醒了操作者。本文介绍用PyQt5监听鼠标的拖拽事件,实现一个类似的效果,当拖拽时动态的改变整个界面边框颜色.下... 查看详情

jquery实现行列的单向固定和列的拖拽

其实这些功能在PL/SQLDev中都有实现,在页面中还是蛮常见的。我实现列的单向固定的原理:将需要单向固定的列放在一个<table>标签中,而整体的数据放在另一个<table>标签中。列的拖拽:使用onstartdrag、ondragover、drop事件... 查看详情

wpf这可能是全网最全的拖拽实现方法的总结

...可能是全网最全的拖拽实现方法的总结前文本文只对笔者学习掌握的一般的拖动问题的实现方法进行整理和讨论,包括窗口、控件等内容的拖动。希望本文能对一些寻找此问题的解决方法的人和一些刚入门的人一些帮助。笔者为... 查看详情

android仿今日头条频道管理(下)(gridview之间item的移动和拖拽)

...。由于自己定义控件大部分要用到事件分发机制的知识。实现思路要实现I 查看详情

基于html5拖拽api实现列表的拖拽排序

基于html5拖拽api实现列表的拖拽排序html代码:<ulondrop="drop_handler(event);"ondragover="dragover_handler(event);"><lidraggable="true"ondragstart="dragstart_handler(event);">1</li><lidraggable="tr 查看详情

pyqt5学习记录---监听鼠标拖拽事件实现mac上百度云盘拖拽效果(代码片段)

背景在Mac版的百度云盘上有这样一个效果,拖拽一个文件过来,将会显示边框蓝色非常醒目的提醒了操作者。本文介绍用PyQt5监听鼠标的拖拽事件,实现一个类似的效果,当拖拽时动态的改变整个界面边框颜色.下... 查看详情

js实现登陆页面的拖拽功能

<!DOCTYPEhtml><html> <head> <metacharset="UTF-8"> <title>登陆页面的拖拽功能实现</title> </head> <styletype="text/css"> *{ margin:0; padding:0; } a{ text-d 查看详情

通过js实现简单的拖拽功能并且可以在特定元素上禁止拖拽

...处理这些细节,经过翻阅jqueryui的源码才找到答案。拖拽实现关于拖拽功能不再啰嗦,直接贴代码/***[draggable拖拽方法]*@param{[type]}modal[移动元素]*@param 查看详情

qt中如何实现一个treewidget的拖拽功能

QTreeWidget中有一个分级别的树,只允许同级别之间的拖拽功能,那位会做帮帮小弟吧参考技术AsetDragDropMode(QAbstractItemView::InternalMove) 查看详情

你还不会antdesign中的拖拽table实现吗(代码片段)

...是现在。歌谣 每天一个前端小知识 提醒你改好好学习了 知乎博主csdn博主b站博主  放弃很容易但是坚持一定很酷     我是歌谣 喜欢就一键三连咯 你得点赞是对歌谣最大的鼓励微信公... 查看详情

jqgrid最近在用jqgrid,我想要实现列的拖拽功能,请问有人实现过吗

我没说清楚,我要实现jqgrid列的拖拽,不是列宽,是列的顺序拖拽。谢谢参考技术A上一章提到在Jqgrid中如何设置二级表头,这一章节主要探讨Jqgrid表格里面的数据如果实现拖动功能,比如你想把第一行的数据拖到当前页的最后一行... 查看详情

android一步一步带你实现recyclerview的拖拽和侧滑删除功能

先上效果图: 本篇文章我们来学习一个开源项目Android-ItemTouchHelper-Demo 这个项目使用了RecyclerView的ItemTouchHelper类实现了Item的拖动和删除功能,ItemTouchHelper是v7包下的一个类,我们看一下他的介绍Thisisautilityclasstoaddswip... 查看详情

javascript实现网页元素的拖拽效果

以下的页面中放了两个div,能够通过鼠标拖拽这两个元素到任何位置。实现该效果的HTML页面代码例如以下所看到的:<!DOCTYPEhtml><html><headlang="en"><metacharset="UTF-8"><title></title><styletype="text/css">#xixi{wid 查看详情

wpf这可能是全网最全的拖拽实现方法的总结

前文本文只对笔者学习掌握的一般的拖动问题的实现方法进行整理和讨论,包括窗口、控件等内容的拖动。希望本文能对一些寻找此问题的解决方法的人和一些刚入门的人一些帮助。笔者为WPF初学者,能得到各位的批评指正也是... 查看详情

javascript实现最简单的拖拽效果

一、一些无关痛痒的唠叨拖拽还是挺不错的一个页面效果,我个人认为,其生命力在于可以让用户自己做一些操作,所谓自定义。例如:①浏览器标签顺序的拖拽切换现在基本上所有的选项卡式的浏览器都有顺序拖拽切换的功能... 查看详情