199.Vue3 + OpenLayers 实现:点击 / 拖动地图播放音频

一、效果说明

本文将实现一个有趣的小功能:

  • 🖱️ 点击地图 → 播放点击音效

  • 🗺️ 拖动地图 → 播放循环音效

  • ⛔ 停止拖动 → 停止音效

适用于:

  • GIS系统交互增强

  • 无人机巡检系统

  • 可视化项目体验优化


二、技术栈

  • Vue3(Composition API)

  • OpenLayers

  • 原生 Audio API


三、实现思路

核心逻辑非常简单:

操作 触发事件 行为
点击地图 click 播放音效
开始拖动 movestart 播放循环音效
结束拖动 moveend 停止音效

四、完整代码

javascript 复制代码
<!--
 * @Author: 彭麒
 * @Date: 2026/3/30
 * @Email: 1062470959@qq.com
 * @Description: 此源码版权归吉檀迦俐所有,可供学习和借鉴或商用。
 -->
<template>
  <div class="container">
    <div class="w-full flex justify-center flex-wrap">
      <div class="font-bold text-[24px]">
        Vue3 + Openlayers:点击/拖动播放音频
      </div>
    </div>
    <div id="map"></div>
  </div>
</template>

<script setup>
import { onMounted, ref } from 'vue'
import 'ol/ol.css'
import { Map, View } from 'ol'
import Tile from 'ol/layer/Tile'
import XYZ from 'ol/source/XYZ'

const map = ref(null)

const clickAudio = new Audio('https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3')
const moveAudio = new Audio('https://interactive-examples.mdn.mozilla.net/media/cc0-audio/t-rex-roar.mp3')

// 循环播放(拖动用)
moveAudio.loop = true

const initMap = () => {
  const raster = new Tile({
    source: new XYZ({
      url: 'https://www.google.com/maps/vt?lyrs=m&gl=en&x={x}&y={y}&z={z}',
      crossOrigin: 'anonymous'
    })
  })

  map.value = new Map({
    target: 'map',
    layers: [raster],
    view: new View({
      projection: 'EPSG:3857',
      center: [2617200, 5951081],
      zoom: 5
    })
  })

  // ✅ 点击播放音效
  map.value.on('click', () => {
    clickAudio.currentTime = 0 // 关键:支持连续点击
    clickAudio.play()
  })

  // ✅ 开始拖动
  map.value.on('movestart', () => {
    if (moveAudio.paused) {
      moveAudio.currentTime = 0
      moveAudio.play()
    }
  })

  // ✅ 拖动结束
  map.value.on('moveend', () => {
    moveAudio.pause()
    moveAudio.currentTime = 0
  })
}

onMounted(() => {
  initMap()
})
</script>

<style scoped>
.container {
  width: 840px;
  height: 590px;
  margin: 50px auto;
  border: 1px solid #42b983;
}

#map {
  width: 800px;
  height: 460px;
  margin: 0 auto;
  border: 1px solid #42b983;
}
</style>

五、关键知识点解析

1️⃣ Vue3 Composition API

复制代码
const map = ref(null)
  • 使用 ref 替代 data

  • 使用 onMounted 替代 mounted


2️⃣ OpenLayers 事件机制

复制代码
map.value.on('click', handler)
map.value.on('movestart', handler)
map.value.on('moveend', handler)

说明:

  • click:点击地图

  • movestart:开始拖动

  • moveend:拖动结束


3️⃣ Audio API 使用

复制代码
const audio = new Audio('xxx.mp3')
audio.play()
audio.pause()

六、常见问题

❗ 1. 连续点击没有声音

复制代码
clickAudio.currentTime = 0

👉 必须重置播放时间


❗ 2. 音频叠加播放

复制代码
if (moveAudio.paused)

👉 防止重复触发播放


❗ 3. 浏览器不播放音频

原因:

  • 浏览器限制自动播放

  • 必须用户交互(点击/拖动)

👉 本案例已符合要求 ✔


❗ 4. 报错:NotSupportedError

复制代码
The element has no supported sources

原因:

  • 音频链接失效

  • 被跨域拦截

  • 不是 MP3 文件

👉 建议:

✔ 使用本地音频

✔ 或使用稳定 HTTPS 音频资源


七、进阶优化

✅ 1. 使用本地音频(生产环境)

复制代码
import clickMp3 from '@/assets/audio/click.mp3'

const clickAudio = new Audio(clickMp3)

✅ 2. 封装音频 Hook

复制代码
const useAudio = () => {
  const play = (audio) => {
    audio.currentTime = 0
    audio.play()
  }
  return { play }
}

✅ 3. 场景升级(推荐方向)

  • 🚁 无人机飞行音效

  • 📡 雷达扫描音

  • 📢 AI语音播报


八、总结

本文实现了一个简单但非常实用的功能:

👉 让地图"有声音"

提升点:

  • 交互更真实

  • 系统更有沉浸感

  • 非常适合 GIS / 可视化系统


九、后续拓展

如果你正在做:

  • 巡检系统

  • 三维 GIS(Cesium)

  • 数字孪生

👉 可以进一步结合:

  • 空间音效

  • 事件驱动音频

  • 语音播报系统


如果觉得有帮助,欢迎点赞 👍 收藏 ⭐ 关注 🚀

后续会持续更新 Vue3 + GIS 实战内容!

相关推荐
csdnfanguyinheng13 分钟前
端到端加密音视频通话系统
音视频
雨田言炎20 分钟前
十、QThread多线程
linux·服务器·开发语言·前端·qt
IT_陈寒20 分钟前
Vite热更新失效?我的几个犯傻操作害我debug两小时
前端·人工智能·后端
晓得迷路了24 分钟前
栗子前端技术周刊第 141 期 - Next.js 16.3、npm 安全事件、2026 CSS 现状调查报告结果...
前端·javascript·npm
2601_9657984738 分钟前
Launch Your Wellness App Fast: The Truth About Meditation Source Code
前端·macos·ios·objective-c·cocoa
周小董1 小时前
[1367]npm 安装 canvas 报错 node-gyp ERR
前端·npm·node.js
智脑API1 小时前
Codex config.toml check_for_update_on_startup 怎么关闭?离线环境与版本提醒排查
运维·服务器·前端
存在的五月雨1 小时前
前端布局-flex
前端
Zzj_tju2 小时前
Tool-Using LLM 论文精读路线:从 ReAct 到可验证工具调用
前端·react.js·前端框架
To_OC9 小时前
别再瞎写 React Router!7 个高频踩坑点一次性讲透
前端·javascript·react.js