拖放与地理定位

2022年05月13日 阅读数:2
这篇文章主要向大家介绍拖放与地理定位,主要内容包括基础应用、实用技巧、原理机制等方面,希望对大家有所帮助。

拖放与地理定位

1. HTML5拖放

1-1 了解拖放

HTML5 提供了 Drag and Drop API,用户可以使用鼠标选择可拖拽(draggable)元素,将元素拖拽到可放置(droppable)元素,并释放鼠标按钮以放置这些元素,拖拽操做期间,会有一个可拖拽元素的半透明快照跟随着鼠标指针javascript

让一个元素被拖拽须要添加 draggable 属性html

  • true —— 元素能够被拖动
  • false —— 元素不能够被拖动
  • auto —— 默认值,浏览器定义的默认行为
<div class="main">
  <div class="box" draggable="true"></div>
  <div class="place-region">请拖到此区域</div>
</div>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Z2f3MzY-1652241463866)(https://raw.githubusercontent.com/xiaofeilalala/DocsPics/main/imgs/20220509085158.png)]java

1-2 拖放事件

HTML5drag & drop 使用了 DOM event model DOM事件模型以及从 mouse events 鼠标事件继承而来的 drag events 拖放事件node

**Tips:**在操做期间,会触发一些事件类型,有一些事件类型可能会被屡次触发git

**Tips:**当从操做系统向浏览器中拖拽文件时,不会触发 dragstartdragend 事件github

事件 On 型事件处理程序 触发时刻
drag ondrag 当拖拽元素或选中的文本时触发
dragend ondragend 当拖拽操做结束时触发 (好比松开鼠标按键或敲“Esc”键)
dragenter ondragenter 当拖拽元素或选中的文本到一个可释放目标时触发
dragexit ondragexit 当元素变得再也不是拖拽操做的选中目标时触发
dragleave ondragleave 当拖拽元素或选中的文本离开一个可释放目标时触发
dragover ondragover 当元素或选中的文本被拖到一个可释放目标上时触发(每 100 毫秒触发一次)
dragstart ondragstart 当用户开始拖拽一个元素或选中的文本时触发(见开始拖拽操做
drop ondrop 当元素或选中的文本在可释放目标上被释放时触发

HTML5 中提供了7个与拖放事件,按照触发顺序:web

  • dragstart —— 当用户开始拖拽一个元素或选中的文本时触发
  • drag —— 当拖拽元素或选中的文本时触发
  • dragenter —— 当拖拽元素或选中的文本到一个可释放目标时触发
  • dragover —— 当元素或选中的文本被拖到一个可释放目标上时触发
  • dragleave —— 当拖拽元素或选中的文本离开一个可释放目标时触发
  • drop —— 当元素或选中的文本在可释放目标上被释放时触发
  • dragend —— 当拖拽操做结束时触发 (松开鼠标按键或敲 Esc 键)

在拖动目标时触发事件,做用在被拖拽元素上:dragstartdragdragendcanvas

释放目标时触发的事件,做用在放置目标元素上:dragenterdragoverdragleavedrop数组

// dragstart 开始拖拽元素时触发
let box = document.querySelector('.box')
let place = document.querySelector('.place-region')

// 拖动目标时触发事件,做用在被拖拽元素上
box.ondragstart = function(e) {
   
   
  console.log('box dragstart')
}

// drag 拖拽元素触发
box.ondrag = function(e) {
   
   
  console.log('box drag')
}

// dragend 拖拽结束时触发
place.ondragend = function(e) {
   
   
  console.log('place dragend')
}

// 释放目标时触发的事件,做用在目标元素上
// dragenter 拖拽到可放置区域时触发
box.ondragenter = function(e) {
   
   
  console.log('box dragenter')
}

// dragover拖拽到可放置区域时触发
place.ondragover = function(e) {
   
   
  e.preventDefault();
  console.log('place dragover')
}

// dragleave 拖拽元素离开可放置区域触发
place.ondragleave = function(e) {
   
   
  console.log('place dragleave')
}

// drop 在可放置区域释放拖拽元素时触发
place.ondrop = function(e) {
   
   
  console.log('place drop')
}

2. 拖放操做

HTML5 的拖拽接口有 DragEvent, DataTransfer, DataTransferItemDataTransferItemList浏览器

2-1 dataTransfer

DragEvent 是一个表示拖、放交互的一个 DOM event 接口,继承 MouseEventEvent 属性

  • dataTransfer —— 在拖放交互期间传输的数据,dataTransfer 属性是一个 DataTransfer 对象
let box = document.querySelector('.box')
box.ondragstart = function(e) {
   
   
  console.log(e.dataTransfer)
}

2-2 拖拽数据

DataTransfer 对象用于保存拖动并放下(drag and drop)过程当中的数据,用于从被拖动元素向放置目标传递字符串数据

**Tips:**必须同时设置 effectAllowed,不然 dropEffect 属性无效

  • DataTransfer.dropEffect 获取当前选定的拖放操做类型或者设置的为一个新的类型。值必须为 none, copy, linkmove
  • DataTransfer.effectAllowed 提供全部可用的操做类型。取值 none, copy, copyLink, copyMove, link, linkMove, move, all or uninitialized 之一
  • files —— 包含数据传输中可用的全部本地文件的列表,若是拖动操做不涉及拖动文件,则此属性为空列表
  • items —— 提供一个包含全部拖动数据列表的 DataTransferItemList 对象
  • types —— 提供 dragstart 事件中设置的格式的 string 数组,若是拖动操做不包含数据,则此数组列表将为空,若是拖动操做中包含任何文件,则其中一个类型将是 Files
// dropEffect属性 获取当前拖拽元素的操做类型或者设置新的类型
let box = document.querySelector('.box')
box.ondragstart = function(e) {
   
   
  e.dataTransfer.setData('text/plan', e.target.className)
  e.dataTransfer.effectAllowed = 'move'
  // 获取容许拖拽元素执行哪几种拖拽效果
  console.log(e.dataTransfer.effectAllowed); // move
  // 获取全部本地文件的列表
  console.log(e.dataTransfer.files); // FileList {length: 0}
}

let place = document.querySelector('.place-region')
place.ondrop = function(e) {
   
   
  e.preventDefault();
  let data = e.dataTransfer.getData('text/plan');
  e.target.innerText = '';
  e.target.prepend(document.querySelector('.' + data))
  // 获取设置的格式
  console.log(e.dataTransfer.types); // ['text/plan']
  // 获取全部拖动数据列表
  console.log(e.dataTransfer.items); // DataTransferItemList {0: DataTransferItem, length: 1}
}

place.ondragover = function(e) {
   
   
  e.preventDefault();
  e.dataTransfer.dropEffect = 'move'
  // 获取拖放操做中浏览器鼠标拖动样式
  console.log(e.dataTransfer.dropEffect); // none
}

2-3 setData

除了 DataTransfer 的原始属性,咱们还使用 setData() 方法,设置自定义拖拽数据项,方法接受两个参数,数据类型和数据值

setData(format, data) —— 设置 DataTransfer 对象指定类型的数据

  • dataTransfer.setData(format, data)format 数据类型,data添加的数据
  • 数据类型为:“text/plain” 和 “text/uri-list
  • 若是该类型的数据不存在,则将其添加到末尾
  • 若是该类型的数据已经存在,则在相同位置替换现有数据
let box = document.querySelector('.box')
box.ondragstart = function(e) {
   
   
  // setData() 设置对象指定拖放类型
  // 两种类型 text/plan text/uri-list
  e.dataTransfer.setData('text/plan', e.target.className)
  e.dataTransfer.setData('text/plan', 'setData方法')
  e.dataTransfer.setData("text/uri-list", "http://www.mozilla.org");
}

2-4 getData

getData(format) —— 检索 DataTransfer 对象指定类型的数据

  • 若是该类型的数据不存在或 datatransfer 不包含数据,则返回空字符串
let box = document.querySelector('.box')
let place = document.querySelector('.place-region')
box.ondragstart = function(e) {
   
   
  // setData() 设置对象指定拖放类型
  // 两种类型 text/plan text/uri-list
  e.dataTransfer.setData('className', e.target.className)
  e.dataTransfer.setData('text/plan', 'setData方法')
  e.dataTransfer.setData("text/uri-list", "http://www.mozilla.org");
}

// getData() 获取对象指定的拖放类型
place.ondrop = function(e) {
   
   
  e.preventDefault();
  // 得到设置的数据项
  console.log(e.dataTransfer.getData('className'))
  console.log(e.dataTransfer.getData('text/plan'))
  console.log(e.dataTransfer.getData('text/uri-list'))
  // 不存在返回空字符串
  console.log(e.dataTransfer.getData('')) 
}

place.ondragover = function(e) {
   
   
  e.preventDefault();
}

2-5 clearData

clearData(format) —— 删除 DataTransfer 对象指定类型关联的数据

  • 该方法只能在 dragstart 事件的处理程序中使用,在拖动操做的数据存储时使用
  • 若是此参数为空字符串或未提供,则将删除全部类型的数据
  • 若是给定类型的数据不存在,则此方法不执行任何操做
let box = document.querySelector('.box')
let place = document.querySelector('.place-region')
box.ondragstart = function(e) {
   
   
  // setData() 设置对象指定拖放类型
  // 两种类型 text/plan text/uri-list
  e.dataTransfer.setData('className', e.target.className)
  e.dataTransfer.setData('text/plan', 'setData方法')
  e.dataTransfer.setData("text/uri-list", "http://www.mozilla.org");

  // clearData 删除指定类型关联的数据
  e.dataTransfer.clearData('className');
}

// getData() 获取对象指定的拖放类型
place.ondrop = function(e) {
   
   
  e.preventDefault();
  // 得到设置的数据项

  // 返回空字符串指定类型关联的数据已被删除
  console.log(e.dataTransfer.getData('className'))
  console.log(e.dataTransfer.getData('text/plan'))
  console.log(e.dataTransfer.getData('text/uri-list'))
  // 不存在返回空字符串
  console.log(e.dataTransfer.getData(''))
}

place.ondragover = function(e) {
   
   
  e.preventDefault();
}

2-6 拖拽图像

当拖拽发生时,会生成拖拽目标的一个半透明图像,并在拖拽过程当中跟踪鼠标指针。这个图像是自动建立的,你可使用 setDragImage() 方法来自定义拖拽反馈图像

setDragImage(img, xOffset, yOffset) —— 用于设置自定义的拖动图像

  • img 设置拖动时半透明的图像,一般是一个 <img> 元素,但也能够是 <canvas> 或任何其余元素
  • xOffsetyOffset 相对于图片的横向纵向偏移量
let box = document.querySelector('.box')
box.ondragstart = function(e) {
   
   
  // setDragImage() 设置拖拽时的图像
  let img = new Image();
  img.src = './assets/zhuang.jpg'
  e.dataTransfer.setDragImage(img, 10, 10)
}

2-7 拖拽效果

dropEffect 属性用来控制拖放操做中用户给予的反馈,它会影响到拖拽过程当中浏览器显示的鼠标样式。在 dragenterdragover 事件期间修改 dropEffect 属性,取值 nonecopymovelink

  • none —— 被拖动元素不能放置在这里
  • copy —— 被拖动元素应该复制到放置目标
  • link —— 在放置目标创建拖动元素的连接
  • move —— 被拖动元素应该移动到放置目标
let box = document.querySelector('.box')
box.ondragstart = function(e) {
   
   
  // effectAllowed属性设置拖动元素容许哪一种效果
  e.dataTransfer.effectAllowed = 'link'
  // none 不容许
  // copy 容许copy样式
  // copylink 容许copy和link样式
  // copyMove 容许copy和move样式
  // link 容许link样式
  // linkMove 容许link与move样式
  // move 容许move样式
  // all 容许全部样式
  // uninitialized 未设置样式默认
}

dragstart 事件监听程序中设置 effectAllowed 属性以指定容许拖拽元素执行哪几种拖拽效果

  • none —— 不容许放下
  • copy —— 容许 copy 放置行为
  • copyLink —— 容许 copylink 放置行为
  • copyMove —— 容许 copymove 放置行为
  • link —— 容许 link 放置行为
  • linkMove —— 容许 linkmove 放置行为
  • move —— 容许 move 放置行为
  • all —— 容许全部放置行为
  • uninitialized —— 未设置放置行为
let place = document.querySelector('.place-region')
// getData() 获取对象指定的拖放类型
place.ondrop = function(e) {
   
   
  e.preventDefault();
}

place.ondragover = function(e) {
   
   
  e.preventDefault();
  // dropEffect 属性用来控制拖放操做中鼠标显示的样式
  // 在dragenter或dragover事件期间修改dropEffect属性
  e.dataTransfer.dropEffect = 'link'
  // none 不能放置拖动元素
  // copy 复制拖动元素到放置目标
  // link 与目标元素创建连接
  // move 移动拖动元素到放置目标
}

2-8 放置对象

当拖拽一个项目到 HTML 元素中时,浏览器默认不会有任何响应,想要让一个元素变成可释放区域,该元素必须在 ondragoverondrop 中阻止默认的处理并设置事件处理

let box = document.querySelector('.box')
let place = document.querySelector('.place-region')
box.ondragstart = function(e) {
   
   
  console.log('dragstart')
}

// 可放置区域必须在元素drop事件与dragover事件取消默认行为
place.ondrop = function(e) {
   
   
  e.preventDefault();
  console.log('drop')
}

place.ondragover = function(e) {
   
   
  e.preventDefault();
  console.log('dragover')
}

2-9 列表拖拽排序

<div class="container">
  <ul class="drag-list">
    <li draggable="true" class="1">HTML</li>
    <li draggable="true" class="2">CSS</li>
    <li draggable="true" class="3">JavaScript</li>
    <li draggable="true" class="4">Vue</li>
    <li draggable="true" class="5">TypeScript</li>
  </ul>
</div>

<script>
  let dragList = document.querySelector('.drag-list')
  let headTitle = document.querySelector('h3')
  let placeList = document.querySelector('.place-list')
  // 获取当前元素的index索引
  function getIndex(elem) {
     
     
    let index = 0;
    if (!elem || !elem.parentNode) {
     
     
      return -1;
    } else {
     
     
      while (elem = elem.previousElementSibling) {
     
     
        index++;
      }
    }
    return index;
  }

  // 当前移动的元素
  let current = null;
  dragList.ondragstart = function (e) {
     
     
    current = e.target
  }

  dragList.ondragover = function (e) {
     
     
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move'
  }

  dragList.ondrop = function (e) {
     
     
    e.preventDefault();
    if (e.target.nodeName === 'LI' && e.target !== current) {
     
     
      // 若是current索引小于当前元素则替换位置
      if (getIndex(current) < getIndex(e.target)) {
     
     
        e.target.after(current)
      } else {
     
     
        e.target.before(current)
      }
    }
  }
</script>

3. DataTransferItem

DataTransferItem 描述了一个拖拽项,在一个拖拽操做*中,*每个 drag event 都有一个 dataTransfer 属性,它包含一个存有拖拽数据的列表,其中每一项都是一个 DataTransferItem

  • kind —— 拖拽项的种类,string 或是 file

  • type —— 拖拽项的类型,通常是一个 MIME 类型

  • getAsFile() —— 返回一个关联拖拽项的 File 对象,当拖拽项不是一个文件时返回 null

  • getAsString() —— 使用拖拽项的字符串做为参数执行指定回调函数

<div class="container">
  <ul class="drag-list">
    <li draggable="true">HTML</li>
    <li draggable="true">CSS</li>
    <li draggable="true">JavaScript</li>
    <li draggable="true">Vue</li>
    <li draggable="true">TypeScript</li>
  </ul>
  <ul class="place-list">可放置区域</ul>
</div>

<script>
  // 获取拖动列表的数量
  let dragList = document.querySelectorAll('.drag-list > li');
  [...dragList].forEach(list => {
     
     
    list.addEventListener('dragstart', function (e) {
     
     
      e.dataTransfer.items.add(this.innerText, 'text/plan');
    })
  })

  let placeList = document.querySelector('.place-list')
  placeList.addEventListener('drop', function (e) {
     
     
    e.preventDefault()
    let data = e.dataTransfer.items;
    // kind 获取拖拽项种类
    console.log(data[0].kind);

    // type 获取拖拽类型
    console.log(data[0].type);

    [...data].forEach(item => {
     
     
      // 拖拽项为file种类的回调函数
      if (item.kind === 'file') {
     
     
        // 返回一个File对象
        console.log(item.getAsFile())
      }
      if (item.kind === 'string') {
     
     
        item.getAsString(function (val) {
     
     
          // 使用拖拽项的字符串做为参数执行指定回调函数
          console.log(val)
        })
      }
    })
  })
  placeList.addEventListener('dragover', function (e) {
     
     
    e.preventDefault()
  })
</script>

4. DataTransferItemList

DataTransferItemList 对象是一组表明被拖动项的 DataTransferItem 对象的列表

  • length —— 列表中拖动项的数量

  • add() —— 向拖动项列表中添加新项 File 对象或 string,该方法返回一个 DataTransferItem 对象

  • remove() —— 根据索引删除拖动项列表中的对象

  • clear() —— 清空拖动项列表

    • 此列表的拖动数据存储区仅在处理 dragstart 事件时可操做

    • 在处理 drop 事件时,drag 数据存储处于只读模式,而此方法不执行任何操做

  • DataTransferItem() —— 返回给定下标的 DataTransferItem 对象

<div class="container">
  <ul class="drag-list">
    <li draggable="true">HTML</li>
    <li draggable="true">CSS</li>
    <li draggable="true">JavaScript</li>
    <li draggable="true">Vue</li>
    <li draggable="true">TypeScript</li>
  </ul>
  <ul class="place-list">可放置区域</ul>
</div>

<script>
  // 获取拖动列表的数量
  let dragList = document.querySelectorAll('.drag-list > li');
  [...dragList].forEach(list => {
     
     
    list.addEventListener('dragstart', function (e) {
     
     
      e.dataTransfer.items.add(this.innerText, 'text/plan');
      if (this.innerText === 'Vue') {
     
     
        e.dataTransfer.setData('text/paln', 'Vue')
      }
      if (this.innerText === 'HTML') {
     
     
        // remove删除拖拽列表中的对象
        e.dataTransfer.items.remove(0)
      }
      if(this.innerText === 'CSS') {
     
     
        // clear() 清空拖动项列表
        e.dataTransfer.items.clear()
      }
    })
  })

  let placeList = document.querySelector('.place-list')
  placeList.addEventListener('drop', function (e) {
     
     
    e.preventDefault()
    let data = e.dataTransfer.items;
    // 拖动列表的length
    console.log(data.length)
    // drop时没法删除拖拽列表只可读
    console.log(data)
  })
  placeList.addEventListener('dragover', function (e) {
     
     
    e.preventDefault()
  })
</script>

5. 地理定位

5-1 geolocation对象

HTML5 Geolocation API 用于得到用户的地理位置

地理位置 API 容许用户向 Web 应用程序提供他们的位置,出于隐私考虑,报告地理位置前会先请求用户许可

//判断地理位置是否支持   
if ("geolocation" in navigator) {
   
   
  /* 地理位置服务可用 */
} else {
   
   
  /* 地理位置服务不可用 */
}

5-2 获取当前定位

能够调用 getCurrentPosition() 函数获取用户当前定位位置

该函数会异步地请求获取用户位置,并查询定位硬件来获取最新信息,当定位被肯定后定义的回调函数就会被执行

navigator.geolocation.getCurrentPosition(success, error, options);
  • success —— 成功获得位置信息时的回调函数,使用 Position 对象做为惟一的参数
// 经过getCurrentPosition()获取当前定位
navigator.geolocation.getCurrentPosition(showPosition)

function showPosition(position) {
   
   
	console.log(position)
	// 经过该函数做为成功获取定位的回调函数
	console.log(position.coords.latitude)
	console.log(position.coords.longitude)
}
  • error —— 可选,用于处理错误,获取位置信息失败时的回调函数,使用 PositionError 对象做为惟一的参数
相关联的常量 描述
1 PERMISSION_DENIED 地理位置信息的获取失败,由于该页面没有获取地理位置信息的权限
2 POSITION_UNAVAILABLE 地理位置获取失败,由于至少有一个内部位置源返回一个内部错误
3 TIMEOUT 获取地理位置超时,经过定义 PositionOptions.timeout 来设置获取地理位置的超时时长
navigator.geolocation.getCurrentPosition(showPosition, showError)
function showError(error) {
   
   
  switch (error.code) {
   
   
    case error.PERMISSION_DENIED:
      console.log('用户拒绝对获取地理位置的请求')
      break;
    case error.POSITION_UNAVAILABLE:
      console.log('位置信息是不可用的')
      break;
    case error.TIMEOUT:
      console.log('请求用户地理位置超时')
      break;
    case error.UNKNOWN_ERROR:
      console.log('未知错误')
      break;
  }
}
  • options —— 可选,一个 PositionOptions 对象
属性 描述
enableHighAccuracy 是否使用其最高精度 false 默认值,设备会经过更快响应、更少的电量等方法来尽量的节约资源
true 提供一个更精确的位置,这会致使较慢的响应时间或者增长电量消耗
timeout 限制返回时间 Infinity 默认值,一直等待到获取位置为止
maximumAge 能够返回多长时间(单位毫秒)内的缓存位置 若是设置为 0, 说明设备不能使用一个缓存位置,并且必须去获取一个真实的当前位置
navigator.geolocation.getCurrentPosition(showPosition, showError, {
   
   
    enableHighAccuracy: true,
    timeout: 2000,
    maximumAge: 2000
})

5-3 监视定位

使用 watchPosition() 函数能够定时获取用户地理位置信息,在用户设备的地理位置发生改变的时候自动被调用

watchPosition()getCurrentPosition() 接受相同的参数,但回调函数会被调用屡次

let watchID = navigator.geolocation.watchPosition(success, error, options);

watchPosition() 函数会返回一个 ID,惟一地标记该位置监视器。您能够将这个 ID 传给 clearWatch() 函数来中止监视用户位置

navigator.geolocation.clearWatch(watchID);
// watchPosition 监视定位当用户位置发生改变时自动调用
let watchID = navigator.geolocation.watchPosition(showPosition, showError, {
   
   
  enableHighAccuracy: false,
  timeout: 5000,
  maximumAge: 0
});
// 中止监听
navigator.geolocation.clearWatch(watchID);

function showError() {
   
   
  console.log('定位失败')
}

function showPosition(position) {
   
   
  console.log(position.coords.latitude)
  console.log(position.coords.longitude)
}

5-4 position对象

获取地理定位成功,则 getCurrentPosition() 方法返回 position 对象。始终会返回 latitudelongitude 以及 accuracy 属性

属性 描述
coords.latitude 十进制数的纬度
coords.longitude 十进制数的经度
coords.accuracy 位置精度
coords.altitude 海拔,海平面以上以米计
coords.altitudeAccuracy 位置的海拔精度
coords.heading 方向,从正北开始以度计
coords.speed 速度,以米/每秒计
timestamp 响应的日期/时间