一、前言
吐槽一下,百度在國內除了百度地圖是良心產品外,其他的真的不敢恭維。在上一篇筆記里,我已經實現了自定義的地圖測量模塊。在百度地圖里面(其他地圖)都有一個周邊搜索的功能,拖拽畫一個圓,然后以圓半徑進行搜索(也就是緩沖區╮(╯_╰)╭)。
這次的目標,就是要山寨這個拖拽畫圓的功能,我先放一個效果圖。
二、開始山寨
我們先想一想要實現這個功能需要哪些步驟。
- 拖拽
- 畫圓
- 通知拖拽結束
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; return new 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) { return new Circle(point, { "radius": distance }); }, _createDragBtnPoint: function (geometry, center) { var extent = geometry.getExtent(); var xmax = extent.xmax; return new 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"); }, }); })
3.小結
本次功能最重要的地方就是實現graphic的拖拽。在拖拽graphic的時候,一定要關閉地圖的導航,把graphic的geomtry設置成當前鼠標的位置。最后,如有不對的地方還請大家批評指正,歡迎轉載!http://www.cnblogs.com/deliciousExtra/p/5503929.html