拖放与地理定位

本文详细介绍了HTML5中的拖放功能,包括如何让元素具备拖放能力、拖放事件、dataTransfer对象的使用,以及如何自定义拖拽图像和效果。此外,还探讨了地理定位API,讲解了geolocation对象的使用,如何获取和监视用户的地理位置信息。

拖放与地理定位

1. HTML5拖放

1-1 了解拖放

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

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

  • 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)]

1-2 拖放事件

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

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

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

事件On 型事件处理程序触发时刻
dragondrag当拖拽元素或选中的文本时触发
dragendondragend当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键)
dragenterondragenter当拖拽元素或选中的文本到一个可释放目标时触发
dragexitondragexit当元素变得不再是拖拽操作的选中目标时触发
dragleaveondragleave当拖拽元素或选中的文本离开一个可释放目标时触发
dragoverondragover当元素或选中的文本被拖到一个可释放目标上时触发(每 100 毫秒触发一次)
dragstartondragstart当用户开始拖拽一个元素或选中的文本时触发(见开始拖拽操作
dropondrop当元素或选中的文本在可释放目标上被释放时触发

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

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

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

释放目标时触发的事件,作用在放置目标元素上: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 对象作为唯一的参数
相关联的常量描述
1PERMISSION_DENIED地理位置信息的获取失败,因为该页面没有获取地理位置信息的权限
2POSITION_UNAVAILABLE地理位置获取失败,因为至少有一个内部位置源返回一个内部错误
3TIMEOUT获取地理位置超时,通过定义 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响应的日期/时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值