leaflet地图实现滑块放大缩小以及拉框放大缩小

前言:

最近公司在做一个有关地图的项目,说到地图,就我个人而言,还没接触过,所以拿到项目的时候是一愣一愣的,心想这都是些啥玩意?没办法,既然项目需要,那就得硬着头皮去干,除非你想卷覆盖走人。

需求:

  1. 拉动滑块实现地图放大缩小
  2. 拉框实现地图放大缩小

过程:

拿到需求的时候第一件事当然是找相关的API啦、找文档啦,公司这边要求使用的地图是leaflets地图,所以就得花时间看看leaflet的相关文档。虽然项目已经做的差不多,当由于产品这边提的两个需求刚好在文档里面找不到,也就是leaflet地图没有提供相关的属性(其实不是没有,只是本人对于这个地图了解得不够深入)。所以只能翻墙找相关案例。

一、leaflet 拉动滑块实现地图放大缩小

这个滑块功能我在leaflet地图上没有找到相关的属性,所以自己琢磨了很久也琢磨不出来。当时有想过自己写一下这个功能的,思路如下:

1. 使用 element ui 里面的滑块并且获取到滑块滚动的事件

2. 把滑块等级设置为32级(因为公司项目的地图的放大缩小支持到32级)

3. 滑块每滑动一次就调用一次对应的放大缩小方法(this.map.zoomin 或 this.map.zoomout )

在这里插入图片描述
后来发现自己能力有限,虽然思路正确了,但只能实现滑块一级一级的滑动,无法实现越级滑动,所以这个思路无法实现(起码本人无法实现)。
后来靠翻墙才找到相关案例(因为leaflet地图是国外开发的,所以案例在外网会比较多)。leaflet滑块,有需要的可以进去看看,不过需要翻墙,不然进不去。

在这里插入图片描述

一、详细写法

  • 如果你使用下面的代码还是无法实现,可以尝试安装一下这个插件 npm i leaflet.zoomslider

js代码区域

L.Control.Zoomslider = (function() {
  var Knob = L.Draggable.extend({
    initialize: function(element, stepHeight, knobHeight) {
      L.Draggable.prototype.initialize.call(this, element, element)
      this._element = element

      this._stepHeight = stepHeight
      this._knobHeight = knobHeight

      this.on('predrag', function() {
        this._newPos.x = 0
        this._newPos.y = this._adjust(this._newPos.y)
      }, this)
    },

    _adjust: function(y) {
      var value = Math.round(this._toValue(y))
      value = Math.max(0, Math.min(this._maxValue, value))
      return this._toY(value)
    },

    // y = k*v + m
    _toY: function(value) {
      return this._k * value + this._m
    },
    // v = (y - m) / k
    _toValue: function(y) {
      return (y - this._m) / this._k
    },

    setSteps: function(steps) {
      var sliderHeight = steps * this._stepHeight
      this._maxValue = steps - 1

      // conversion parameters
      // the conversion is just a common linear function.
      this._k = -this._stepHeight
      this._m = sliderHeight - (this._stepHeight + this._knobHeight) / 2
    },

    setPosition: function(y) {
      L.DomUtil.setPosition(this._element,
        L.point(0, this._adjust(y)))
    },

    setValue: function(v) {
      this.setPosition(this._toY(v))
    },

    getValue: function() {
      return this._toValue(L.DomUtil.getPosition(this._element).y)
    }
  })

  var Zoomslider = L.Control.extend({
    options: {
      position: 'topleft',
      // Height of zoom-slider.png in px
      stepHeight: 6,
      // Height of the knob div in px (including border)
      knobHeight: 4,
      styleNS: 'leaflet-control-zoomslider'
    },

    onAdd: function(map) {
      this._map = map
      this._ui = this._createUI()
      this._knob = new Knob(this._ui.knob, this.options.stepHeight, this.options.knobHeight)

      map.whenReady(this._initKnob, this)
        .whenReady(this._initEvents, this)
        .whenReady(this._updateSize, this)
        .whenReady(this._updateKnobValue, this)
        .whenReady(this._updateDisabled, this)
      return this._ui.bar
    },

    onRemove: function(map) {
      map.off('zoomlevelschange', this._updateSize, this)
        .off('zoomend zoomlevelschange', this._updateKnobValue, this)
        .off('zoomend zoomlevelschange', this._updateDisabled, this)
    },

    _createUI: function() {
      var ui = {}
      var ns = this.options.styleNS

      ui.bar = L.DomUtil.create('div', ns + ' leaflet-bar'),
      ui.zoomIn = this._createZoomBtn('in', 'top', ui.bar),
      ui.wrap = L.DomUtil.create('div', ns + '-wrap leaflet-bar-part', ui.bar),
      ui.zoomOut = this._createZoomBtn('out', 'bottom', ui.bar),
      ui.body = L.DomUtil.create('div', ns + '-body', ui.wrap),
      ui.knob = L.DomUtil.create('div', ns + '-knob')

      L.DomEvent.disableClickPropagation(ui.bar)
      L.DomEvent.disableClickPropagation(ui.knob)

      return ui
    },
    _createZoomBtn: function(zoomDir, end, container) {
      var classDef = this.options.styleNS + '-' + zoomDir +
					' leaflet-bar-part' +
					' leaflet-bar-part-' + end
      var link = L.DomUtil.create('a', classDef, container)

      link.href = '#'
      link.title = 'Zoom ' + zoomDir

      L.DomEvent.on(link, 'click', L.DomEvent.preventDefault)

      return link
    },

    _initKnob: function() {
      this._knob.enable()
      this._ui.body.appendChild(this._ui.knob)
    },
    _initEvents: function(map) {
      this._map
        .on('zoomlevelschange', this._updateSize, this)
        .on('zoomend zoomlevelschange', this._updateKnobValue, this)
        .on('zoomend zoomlevelschange', this._updateDisabled, this)

      L.DomEvent.on(this._ui.body, 'click', this._onSliderClick, this)
      L.DomEvent.on(this._ui.zoomIn, 'click', this._zoomIn, this)
      L.DomEvent.on(this._ui.zoomOut, 'click', this._zoomOut, this)

      this._knob.on('dragend', this._updateMapZoom, this)
    },

    _onSliderClick: function(e) {
      var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e)
      var y = L.DomEvent.getMousePosition(first, this._ui.body).y

      this._knob.setPosition(y)
      this._updateMapZoom()
    },

    _zoomIn: function(e) {
      this._map.zoomIn(e.shiftKey ? 3 : 1)
    },
    _zoomOut: function(e) {
      this._map.zoomOut(e.shiftKey ? 3 : 1)
    },

    _zoomLevels: function() {
      var zoomLevels = this._map.getMaxZoom() - this._map.getMinZoom() + 1
      return zoomLevels < Infinity ? zoomLevels : 0
    },
    _toZoomLevel: function(value) {
      return value + this._map.getMinZoom()
    },
    _toValue: function(zoomLevel) {
      return zoomLevel - this._map.getMinZoom()
    },

    _updateSize: function() {
      var steps = this._zoomLevels()

      this._ui.body.style.height = this.options.stepHeight * steps + 'px'
      this._knob.setSteps(steps)
    },
    _updateMapZoom: function() {
      this._map.setZoom(this._toZoomLevel(this._knob.getValue()))
    },
    _updateKnobValue: function() {
      this._knob.setValue(this._toValue(this._map.getZoom()))
    },
    _updateDisabled: function() {
      var zoomLevel = this._map.getZoom()
      var className = this.options.styleNS + '-disabled'

      L.DomUtil.removeClass(this._ui.zoomIn, className)
      L.DomUtil.removeClass(this._ui.zoomOut, className)

      if (zoomLevel === this._map.getMinZoom()) {
        L.DomUtil.addClass(this._ui.zoomOut, className)
      }
      if (zoomLevel === this._map.getMaxZoom()) {
        L.DomUtil.addClass(this._ui.zoomIn, className)
      }
    }
  })

  return Zoomslider
})()

L.Map.mergeOptions({
  zoomControl: false,
  zoomsliderControl: true
})

L.Map.addInitHook(function() {
  if (this.options.zoomsliderControl) {
    this.zoomsliderControl = new L.Control.Zoomslider()
    this.addControl(this.zoomsliderControl)
  }
})

L.control.zoomslider = function(options) {
  return new L.Control.Zoomslider(options)
}

css代码区域

/** Slider **/
.leaflet-control-zoomslider-wrap {
	padding-top: 5px;
	padding-bottom: 5px;
	background-color: #fff;
	border-bottom: 1px solid #ccc;
}
.leaflet-control-zoomslider-body {
	width: 22px;
	border: solid #fff;
	border-width: 0px 9px 0px 9px;
	background-color: rgb(179, 178, 178);
	margin: 0 auto;
}
.leaflet-control-zoomslider-knob {
	position: relative;
	width: 12px;
	height: 4px;
	background-color: #efefef;
	-webkit-border-radius: 2px;
	border-radius: 2px;
	border: 1px solid rgb(179, 178, 178);
	margin-left: -4px;
}
.leaflet-control-zoomslider-body:hover {
	cursor: pointer;
}
.leaflet-control-zoomslider-knob:hover {
	cursor: default;
	cursor: -webkit-grab;
	cursor:    -moz-grab;
}

.leaflet-dragging .leaflet-control-zoomslider,
.leaflet-dragging .leaflet-control-zoomslider-wrap,
.leaflet-dragging .leaflet-control-zoomslider-body,
.leaflet-dragging .leaflet-control-zoomslider a,
.leaflet-dragging .leaflet-control-zoomslider a.leaflet-control-zoomslider-disabled,
.leaflet-dragging .leaflet-control-zoomslider-knob:hover  {
	cursor: move;
	cursor: -webkit-grabbing;
	cursor:    -moz-grabbing;
}

/** Leaflet Zoom Styles **/
.leaflet-container .leaflet-control-zoomslider {
	margin-left: 10px;
	margin-top: 50px;
}
.leaflet-control-zoomslider a {
	width: 24px;
	height: 26px;
	text-align: center;
	text-decoration: none;
	color: black; 
	display: block;
}
.leaflet-control-zoomslider a:hover {
	width: 24px;
	background-color: #f4f4f4;
}
.leaflet-control-zoomslider-in {
	font: bold 18px 'Lucida Console', Monaco, monospace;
}
.leaflet-control-zoomslider-in:after{
	content:"+"
}
.leaflet-control-zoomslider-out {
	font: bold 22px 'Lucida Console', Monaco, monospace;
}
.leaflet-control-zoomslider-out:after{
	content:"-"
}
.leaflet-control-zoomslider a.leaflet-control-zoomslider-disabled {
	cursor: default;
	color: #bbb;
}

/* Touch */
.leaflet-touch .leaflet-control-zoomslider-body {
	background-position: 10px 0px;
}
.leaflet-touch .leaflet-control-zoomslider-knob {
	width: 16px;
	margin-left: -7px;
}
.leaflet-touch .leaflet-control-zoomslider a {
	width: 30px;
	line-height: 30px;
}
.leaflet-touch .leaflet-control-zoomslider a:hover {
	width: 30px;
	line-height: 30px;
}
.leaflet-touch .leaflet-control-zoomslider-in {
	font-size: 24px;
	line-height: 29px;
}
.leaflet-touch .leaflet-control-zoomslider-out {
	font-size: 28px;
	line-height: 30px;
}
.leaflet-touch .leaflet-control-zoomslider {
	box-shadow: none;
	border: 4px solid rgba(0,0,0,0.3);
}

二、简单粗暴法
对于看不懂的或者想偷懒的程序猿来说,复制粘贴是最快的方法,如果你不想动脑筋,可以把下面的两个文件直接复制粘贴到你的项目里面去,然后直接引用就可以了
滑块放大缩小的css
滑块放大缩小的js
引用:

  • 在你的vue文件里把对应的js文件与css文件引入即可

在这里插入图片描述

效果:

在这里插入图片描述

二、leaflet 拉框放大

对于拉框放大,相信很多人都能从leaflet的相关文档里面找到它的相关属性,都知道按着Shift键加鼠标就可以实现拉框放大,但用户不知道啊,所以我们得把它显示到界面上。但leaflet只提供了相关的属性,并没有提供相关的用法,这就让人很苦恼。你不可能把Shift键当成一个按钮显示到页面上,当用户点击时按钮状态一直为true,这样的话如果Shift键在这时有什么用处,那这就会产生冲突,这明显不是最佳的解决方法。下面,我就给大家呈上正确的方法。

案例

leaflet拉框缩小

一、详细解决方法

js代码区域

/*
 * L.Control.BoxZoom
 * A visible, clickable control for doing a box zoom.
 * https://github.com/gregallensworth/L.Control.BoxZoom
 */
L.Control.BoxZoom = L.Control.extend({
    options: {
        position: 'topright',
        title: 'Click here then draw a square on the map, to zoom in to an area',
        aspectRatio: null,
        divClasses: '',
        enableShiftDrag: false,
        iconClasses: '',
        keepOn: true,
    },
    initialize: function(options) {
        L.setOptions(this, options);
        this.map    = null;
        this.active = false;
    },
    onAdd: function (map) {
        // add a linkage to the map, since we'll be managing map layers
        this.map = map;
        this.active = false;

        // create our button: uses FontAwesome cuz that font is... awesome
        // assign this here control as a property of the visible DIV, so we can be more terse when writing click-handlers on that visible DIV
        this.controlDiv = L.DomUtil.create('div', 'leaflet-control-boxzoom');

        // if we're not using an icon, add the background image class
        if (!this.options.iconClasses) {
            L.DomUtil.addClass(this.controlDiv, 'with-background-image');
        }
        if (this.options.divClasses) {
            L.DomUtil.addClass(this.controlDiv, this.options.divClasses);
        }
        this.controlDiv.control   = this;
        this.controlDiv.title     = this.options.title;
        this.controlDiv.innerHTML = ' ';
        L.DomEvent
            .addListener(this.controlDiv, 'mousedown', L.DomEvent.stopPropagation)
            .addListener(this.controlDiv, 'click', L.DomEvent.stopPropagation)
            .addListener(this.controlDiv, 'click', L.DomEvent.preventDefault)
            .addListener(this.controlDiv, 'click', function () {
                this.control.toggleState();
            });

        // start by toggling our state to off; this disables the boxZoom hooks on the map, in favor of this one
        this.setStateOff();

        if (this.options.iconClasses) {
            var iconElement = L.DomUtil.create('i', this.options.iconClasses, this.controlDiv);
            if (iconElement) {
                iconElement.style.color = this.options.iconColor || 'black';
                iconElement.style.textAlign = 'center';
                iconElement.style.verticalAlign = 'middle';
            } else {
                console.log('Unable to create element for icon');
            }
        }

        // if we're enforcing an aspect ratio, then monkey-patch the map's real BoxZoom control to support that
        // after all, this control is just a wrapper over the map's own BoxZoom behavior
        if (this.options.aspectRatio) {
            this.map.boxZoom.aspectRatio = this.options.aspectRatio;
            this.map.boxZoom._onMouseMove = this._boxZoomControlOverride_onMouseMove;
            this.map.boxZoom._onMouseUp = this._boxZoomControlOverride_onMouseUp;
        }

        // done!
        return this.controlDiv;
    },

    onRemove: function (map) {
        // on remove: if we had to monkey-patch the aspect-ratio stuff, undo that now
        if (this.options.aspectRatio) {
            delete this.map.boxZoom.aspectRatio;
            this.map.boxZoom._onMouseMove = L.Map.BoxZoom.prototype._onMouseMove;
            this.map.boxZoom._onMouseUp = L.Map.BoxZoom.prototype._onMouseUp;
        }
    },

    toggleState: function () {
        this.active ? this.setStateOff() : this.setStateOn();
    },
    setStateOn: function () {
        L.DomUtil.addClass(this.controlDiv,'leaflet-control-boxzoom-active');
        this.active = true;
        this.map.dragging.disable();
        if (!this.options.enableShiftDrag) {
            this.map.boxZoom.addHooks();
        }

        this.map.on('mousedown', this.handleMouseDown, this);
        if (!this.options.keepOn) {
            this.map.on('boxzoomend', this.setStateOff, this);
        }

        L.DomUtil.addClass(this.map._container,'leaflet-control-boxzoom-active');
    },
    setStateOff: function () {
        L.DomUtil.removeClass(this.controlDiv,'leaflet-control-boxzoom-active');
        this.active = false;
        this.map.off('mousedown', this.handleMouseDown, this);
        this.map.dragging.enable();
        if (!this.options.enableShiftDrag) {
            this.map.boxZoom.removeHooks();
        }

        L.DomUtil.removeClass(this.map._container,'leaflet-control-boxzoom-active');
    },

    handleMouseDown: function (event) {
        this.map.boxZoom._onMouseDown.call(this.map.boxZoom, { clientX:event.originalEvent.clientX, clientY:event.originalEvent.clientY, which:1, shiftKey:true });
    },

    // monkey-patched applied to L.Map.BoxZoom to handle aspectRatio and to zoom to the drawn box instead of the mouseEvent point
    // in these methods,  "this" is not the control, but the map's boxZoom instance
	_boxZoomControlOverride_onMouseMove: function (e) {
		if (!this._moved) {
			this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane);
			L.DomUtil.setPosition(this._box, this._startLayerPoint);

			//TODO refactor: move cursor to styles
			this._container.style.cursor = 'crosshair';
			this._map.fire('boxzoomstart');
		}

		var startPoint = this._startLayerPoint,
		    box = this._box,

		    layerPoint = this._map.mouseEventToLayerPoint(e),
		    offset = layerPoint.subtract(startPoint),

		    newPos = new L.Point(
		        Math.min(layerPoint.x, startPoint.x),
		        Math.min(layerPoint.y, startPoint.y));

		L.DomUtil.setPosition(box, newPos);

		this._moved = true;

        var width = (Math.max(0, Math.abs(offset.x) - 4));  // from L.Map.BoxZoom, TODO refactor: remove hardcoded 4 pixels
        var height = (Math.max(0, Math.abs(offset.y) - 4));  // from L.Map.BoxZoom, TODO refactor: remove hardcoded 4 pixels

        if (this.aspectRatio) {
            height = width / this.aspectRatio;
        }

		box.style.width  = width + 'px';
		box.style.height = height + 'px';
	},
	_boxZoomControlOverride_onMouseUp: function (e) {
        // the stock behavior is to generate a bbox based on the _startLayerPoint and the mouseUp event point
        // we don't want that; we specifically want to use the drawn box with the fixed aspect ratio

        // fetch the box and convert to a map bbox, before we clear it
        var ul = this._box._leaflet_pos;
        var lr = new L.Point(this._box._leaflet_pos.x + this._box.offsetWidth, this._box._leaflet_pos.y + this._box.offsetHeight);
        var nw = this._map.layerPointToLatLng(ul);
        var se = this._map.layerPointToLatLng(lr);
        if (nw.equals(se)) { return; }

		this._finish();

		var bounds = new L.LatLngBounds(nw, se);
		this._map.fitBounds(bounds);

		this._map.fire('boxzoomend', {
			boxZoomBounds: bounds
		});
	},
});
L.Control.boxzoom = function(options) {
    return new L.Control.BoxZoom(options);
}

css代码区域

/*
 * BOXZOOM
 */
.leaflet-control-boxzoom {
    background-color:white;
    border-radius:4px;
    border:1px solid #cccccc;
    width:25px;
    height:25px;
    line-height:25px;
    box-shadow:0 1px 2px rgba(0,0,0,0.65);
    cursor:pointer !important;
}

.with-background-image {
    background-image:url(leaflet-control-boxzoom.svg);
    background-repeat:no-repeat;
    background-size:21px 21px; /* 25px image, 25px box; subtract 2px for padding on every side = 21px rendering height */
    background-position:2px 2px;
}

.leaflet-control-boxzoom.leaflet-control-boxzoom-active {
    background-color:#aaaaaa;
}

.leaflet-container.leaflet-control-boxzoom-active, .leaflet-container.leaflet-control-boxzoom-active path.leaflet-interactive {
    cursor:crosshair !important;
}

.leaflet-control-boxzoom i {
    display:block;
}

.leaflet-control-boxzoom i.icon {
    font-size:17px;
    margin-left:1px;
    margin-top:3px;
}

.leaflet-control-boxzoom i.fa {
    margin-top:6px;
}
.leaflet-control-boxzoom i.glyphicon {
    margin-top:5px;
}

二,快速解决法

对于懒惰的人来说,我还给他准备了快速的解决方法,直接复制粘贴(注意:文件都是配套的,不可复制少文件)

拉框放大的js文件

拉框放大的css文件

拉框放大的svg文件

引用方法
与滑块引用方法一样,把代码路径引进来
在这里插入图片描述
再到相关配置文件里面把它 addTo 到地图上面去,就可以正常使用啦。

L.Control.boxzoom({ position: ‘topleft’ }).addTo(map)

效果:

在这里插入图片描述
对于拉框缩小来说,鄙人还没有找到相关的属于以及解决方法,等后续找到相关方法,再给大家接上。或者你已经有解决方法,也麻烦教一下,毕竟甲方需求必须完成,谢谢。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

别来…无恙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值