广告
返回顶部
首页 > 资讯 > 精选 >Vue如何实现悬浮框自由移动+录音功能
  • 731
分享到

Vue如何实现悬浮框自由移动+录音功能

2023-07-02 17:07:01 731人浏览 安东尼
摘要

本篇内容介绍了“Vue如何实现悬浮框自由移动+录音功能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!效果如下主要功能一个漂浮的球,在全屏幕中

本篇内容介绍了“Vue如何实现悬浮框自由移动+录音功能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

效果如下

Vue如何实现悬浮框自由移动+录音功能

Vue如何实现悬浮框自由移动+录音功能

主要功能

一个漂浮的球,在全屏幕中自由移动遇到边边角角自动改变方向 ,自带动画效果

录音功能,可以录制用户的声音,可以下载为任意格式的音频文件到本地,也可以通过二进制流发给后端

由于后端要声音文件格式wav或者pcm,采样率16000,所以我改了配置文件,稍后我会介绍在哪里改,改什么什么样都是可以的。

注:代码我已经封装成组件了,下方的代码可以直接稍作修改后拿去用,需要修改的地方我以截图的形式贴出来了。

实现

1.封装第一个漂浮组件FloatBall.vue

<template>  <!--悬浮小广告样式的提示信息-->  <div    id="thediv"    ref="thediv"        @mouseover="clearFdAd"    @mouseout="starFdAd"  >    <div      style="        overflow: hidden;        cursor: pointer;        text-align: right;        font-size: 0.0625rem;        color: #999999;      "    >      <div @click="demo" >        <!-- 录音组件 -->        <div v-show="isAudio">          <Audio ></Audio>        </div>        <div class="loader">          <span></span>          <span></span>          <span></span>          <span></span>          <span></span>          <span></span>          <span></span>          <span></span>          <span></span>          <span></span>        </div>        <!-- <img src="@/assets/common/loginloGo.png" alt="" srcset="" /> -->      </div>    </div>    <!-- <a      href="Http://xxxxxx" target="_blank""    ><img src="../../assets/images/tips.png" width="320" border="0" /></a> -->  </div></template><script> var intervalexport default {  data () {    return {      isAudio: false,      xPos: 0,      yPos: 0,      xin: true,      yin: true,      step: 1,      delay: 18,      height: 0,      Hoffset: 0,      Woffset: 0,      yon: 0,      xon: 0,      pause: true,      thedivShow: true,    }  },   mounted () {    interval = setInterval(this.changePos, this.delay)  },   methods: {    demo () {      this.isAudio = !this.isAudio    },    changePos () {      let width = document.documentElement.clientWidth      let height = document.documentElement.clientHeight      this.Hoffset = this.$refs.thediv.clientHeight//获取元素高度      this.Woffset = this.$refs.thediv.offsetWidth       // 滚动部分跟随屏幕滚动      // this.$refs.thediv.style.left = (this.xPos + document.body.scrollLeft + document.documentElement.scrollLeft) + "px";      // this.$refs.thediv.style.top = (this.yPos + document.body.scrollTop + document.documentElement.scrollTop) + "px";       // 滚动部分不随屏幕滚动      this.$refs.thediv.style.left =        this.xPos + document.body.scrollLeft - 400 + "px"      this.$refs.thediv.style.top = this.yPos + document.body.scrollTop + "px"       if (this.yon) {        this.yPos = this.yPos + this.step      } else {        this.yPos = this.yPos - this.step      }      if (this.yPos < 0) {        this.yon = 1        this.yPos = 0      }      if (this.yPos >= height - this.Hoffset) {        this.yon = 0        this.yPos = height - this.Hoffset      }       if (this.xon) {        this.xPos = this.xPos + this.step      } else {        this.xPos = this.xPos - this.step      }      if (this.xPos < 0) {        this.xon = 1        this.xPos = 0      }      if (this.xPos >= width - this.Woffset) {        this.xon = 0        this.xPos = width - this.Woffset      }    },    clearFdAd () {      clearInterval(interval)    },    starFdAd () {      interval = setInterval(this.changePos, this.delay)    },  },};</script> <style lang="sCSS" scoped>#thediv {  z-index: 100;  position: absolute;  top: 0.224rem;  left: 0.0104rem;  height: 0.9583rem;  width: 1.4583rem;  overflow: hidden;  img {    width: 100%;    height: 100%;  }}// 以下是css图标.loader {  width: 100px;  height: 100px;  padding: 30px;  font-size: 10px;  background-color: #91cc75;  border-radius: 50%;  border: 8px solid #20a088;  display: flex;  align-items: center;  justify-content: space-between;  animation: loader-animate 1.5s infinite ease-in-out;} @keyframes loader-animate {  45%,  55% {    transfORM: scale(1);  }} .loader > span {  width: 5px;  height: 50%;  background-color: #fff;  transform: scaleY(0.05) translateX(-5px);  animation: span-animate 1.5s infinite ease-in-out;  animation-delay: calc(var(--n) * 0.05s);} @keyframes span-animate {  0%,  100% {    transform: scaleY(0.05) translateX(-5px);  }  15% {    transform: scaleY(1.2) translateX(10px);  }  90%,  100% {    background-color: hotpink;  }} .loader > span:nth-child(1) {  --n: 1;}.loader > span:nth-child(2) {  --n: 2;}.loader > span:nth-child(3) {  --n: 3;}.loader > span:nth-child(4) {  --n: 4;}.loader > span:nth-child(5) {  --n: 5;}.loader > span:nth-child(6) {  --n: 6;}.loader > span:nth-child(7) {  --n: 7;}.loader > span:nth-child(8) {  --n: 8;}.loader > span:nth-child(9) {  --n: 9;}.loader > span:nth-child(10) {  --n: 10;}</style>

2.封装第二个组件录音组件Audio.vue

这个组件依赖另一个文件recorder.js,在最下方贴出来了。

<template>  <div>    <el-button @click="myrecording" >{{      time    }}</el-button>    <el-button @click="startPlay" >{{      playing ? "播放" : "暂停"    }}</el-button>    <el-button @click="delvioce"       >删除</el-button    >    <audio      v-if="fileurl"      :src="fileurl"      controls="controls"            ref="audio"      id="myaudio"    ></audio>  </div></template><script>// 引入recorder.jsimport recording from "@/utils/recorder"export default {  data () {    return {      RecordingSwitch: true, //录音开关      files: "", //语音文件      num: 60, // 按住说话时间      recorder: null,      fileurl: "", //语音URL      interval: "", //定时器      time: "点击说话(60秒)",      playing: true,    }  },   methods: {    // 点击录制    myrecording () {      if (this.files === "") {        if (this.RecordingSwitch) {          this.Start()        } else {          this.End()        }      } else if (this.time === "点击重录(60秒)") {        this.files = ""        this.Start()      }      this.RecordingSwitch = !this.RecordingSwitch    },    // 点击播放    startPlay () {      console.dir(this.$refs.audio, '--------------------')      if (this.playing) {        this.$refs.audio.play()      } else {        this.$refs.audio.pause()      }      this.playing = !this.playing    },    // 删除语音    delvioce () {      this.fileurl = ""      this.files = ""      this.num = 60      this.time = "点击说话(60秒)"    },    // 清除定时器    clearTimer () {      if (this.interval) {        this.num = 60        clearInterval(this.interval)      }    },    // 开始录制    Start () {      this.clearTimer()      recording.get((rec) => {        // 当首次按下时,要获取浏览器的麦克风权限,所以这时要做一个判断处理        if (rec) {          this.recorder = rec          this.interval = setInterval(() => {            if (this.num <= 0) {              this.recorder.stop()              this.num = 60              this.End()            } else {              this.time = "点击结束(" + this.num + "秒)"              this.recorder.start()              this.num--            }          }, 1000)        }      })    },    // 停止录制    End () {      this.clearTimer()      if (this.recorder) {        this.recorder.stop() // 重置说话时间        this.num = 60        this.time = "点击重录(60秒)" // 获取语音二进制文件        let bold = this.recorder.getBlob() // 将获取的二进制对象转为二进制文件流        let files = new File([bold], "test.wav", {          type: "audio/wav",          lastModified: Date.now(),        })        this.files = files        console.log(this.files, '----------', bold, '---------------', this.recorder)        //获取音频时长        let fileurl = URL.createObjectURL(files)        this.fileurl = fileurl        let audioElement = new Audio(fileurl)        let duration        audioElement.addEventListener("loadedmetadata", function (_event) {          duration = audioElement.duration          console.log("视频时长:" + duration, files)        })        var downloadAnchornode = document.createElement('a')        downloadAnchorNode.setAttribute("href", fileurl)        downloadAnchorNode.setAttribute("download", 'ssss')        downloadAnchorNode.click()        downloadAnchorNode.remove()        this.$message.success("正在下载中,请稍后...")      }    },  },};</script><style lang="less" ></style>

3.recorder.js

Vue如何实现悬浮框自由移动+录音功能

Vue如何实现悬浮框自由移动+录音功能

// 兼容window.URL = window.URL || window.WEBkitURLnavigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedialet HZRecorder = function (stream, config) {  config = config || {}  config.sampleBits = config.sampleBits || 8 // 采样数位 8, 16  config.sampleRate = config.sampleRate || (16000) // 采样率(1/6 44100)  let context = new (window.webkitAudioContext || window.AudioContext)()  let audioInput = context.createMediaStreamSource(stream)  let createScript = context.createScriptProcessor || context.createjavascriptNode  let recorder = createScript.apply(context, [4096, 1, 1])  let audioData = {    size: 0, // 录音文件长度    buffer: [], // 录音缓存    inputSampleRate: context.sampleRate, // 输入采样率    inputSampleBits: 16, // 输入采样数位 8, 16    outputSampleRate: config.sampleRate, // 输出采样率    oututSampleBits: config.sampleBits, // 输出采样数位 8, 16    input: function (data) {      this.buffer.push(new Float32Array(data))      this.size += data.length    },    compress: function () { // 合并压缩      // 合并      let data = new Float32Array(this.size)      let offset = 0      for (let i = 0; i < this.buffer.length; i++) {        data.set(this.buffer[i], offset)        offset += this.buffer[i].length      }      // 压缩      let compression = parseInt(this.inputSampleRate / this.outputSampleRate)      let length = data.length / compression      let result = new Float32Array(length)      let index = 0; let j = 0      while (index < length) {        result[index] = data[j]        j += compression        index++      }      return result    },    encodeWAV: function () {      let sampleRate = Math.min(this.inputSampleRate, this.outputSampleRate)      let sampleBits = Math.min(this.inputSampleBits, this.oututSampleBits)      let bytes = this.compress()      let dataLength = bytes.length * (sampleBits / 8)      let buffer = new ArrayBuffer(44 + dataLength)      let data = new DataView(buffer)      let channelCount = 1// 单声道      let offset = 0      let writeString = function (str) {        for (let i = 0; i < str.length; i++) {          data.setUint8(offset + i, str.charCodeAt(i))        }      }      // 资源交换文件标识符      writeString('RIFF'); offset += 4      // 下个地址开始到文件尾总字节数,即文件大小-8      data.setUint32(offset, 36 + dataLength, true); offset += 4      // WAV文件标志      writeString('WAVE'); offset += 4      // 波形格式标志      writeString('fmt '); offset += 4      // 过滤字节,一般为 0x10 = 16      data.setUint32(offset, 16, true); offset += 4      // 格式类别 (PCM形式采样数据)      data.setUint16(offset, 1, true); offset += 2      // 通道数      data.setUint16(offset, channelCount, true); offset += 2      // 采样率,每秒样本数,表示每个通道的播放速度      data.setUint32(offset, sampleRate, true); offset += 4      // 波形数据传输率 (每秒平均字节数) 单声道×每秒数据位数×每样本数据位/8      data.setUint32(offset, channelCount * sampleRate * (sampleBits / 8), true); offset += 4      // 快数据调整数 采样一次占用字节数 单声道×每样本的数据位数/8      data.setUint16(offset, channelCount * (sampleBits / 8), true); offset += 2      // 每样本数据位数      data.setUint16(offset, sampleBits, true); offset += 2      // 数据标识符      writeString('data'); offset += 4      // 采样数据总数,即数据总大小-44      data.setUint32(offset, dataLength, true); offset += 4      // 写入采样数据      if (sampleBits === 8) {        for (let i = 0; i < bytes.length; i++, offset++) {          let s = Math.max(-1, Math.min(1, bytes[i]))          let val = s < 0 ? s * 0x8000 : s * 0x7FFF          val = parseInt(255 / (65535 / (val + 32768)))          data.setInt8(offset, val, true)        }      } else {        for (let i = 0; i < bytes.length; i++, offset += 2) {          let s = Math.max(-1, Math.min(1, bytes[i]))          data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)        }      }      return new Blob([data], { type: 'audio/mp3' })    }  }  // 开始录音  this.start = function () {    audioInput.connect(recorder)    recorder.connect(context.destination)  }  // 停止  this.stop = function () {    recorder.disconnect()  }  // 获取音频文件  this.getBlob = function () {    this.stop()    return audioData.encodeWAV()  }  // 回放  this.play = function (audio) {    let downRec = document.getElementById('downloadRec')    downRec.href = window.URL.createObjectURL(this.getBlob())    downRec.download = new Date().toLocaleString() + '.mp3'    audio.src = window.URL.createObjectURL(this.getBlob())  }  // 上传  this.upload = function (url, callback) {    let fd = new FormData()    fd.append('audioData', this.getBlob())    let xhr = new XMLHttpRequest()        if (callback) {      xhr.upload.addEventListener('progress', function (e) {        callback('uploading', e)      }, false)      xhr.addEventListener('load', function (e) {        callback('ok', e)      }, false)      xhr.addEventListener('error', function (e) {        callback('error', e)      }, false)      xhr.addEventListener('abort', function (e) {        callback('cancel', e)      }, false)    }        xhr.open('POST', url)    xhr.send(fd)  }  // 音频采集  recorder.onaudioprocess = function (e) {    audioData.input(e.inputBuffer.getChannelData(0))    // record(e.inputBuffer.getChannelData(0));  }}// 抛出异常HZRecorder.throwError = function (message) {  alert(message)  throw new function () { this.toString = function () { return message } }()}// 是否支持录音HZRecorder.canRecording = (navigator.getUserMedia != null)// 获取录音机HZRecorder.get = function (callback, config) {  if (callback) {    if (navigator.getUserMedia) {      console.log(navigator)      navigator.getUserMedia(        { audio: true } // 只启用音频        , function (stream) {          let rec = new HZRecorder(stream, config)          callback(rec)        }        , function (error) {          console.log(error)          switch (error.code || error.name) {            case 'PERMISSION_DENIED':            case 'PermissionDeniedError':              HZRecorder.throwError('用户拒绝提供信息。')              break            case 'NOT_SUPPORTED_ERROR':            case 'NotSupportedError':              HZRecorder.throwError('浏览器不支持硬件设备。')              break            case 'MANDATORY_UNSATISFIED_ERROR':            case 'MandatoryUnsatisfiedError':              HZRecorder.throwError('无法发现指定的硬件设备。')              break            default:              HZRecorder.throwError('无法打开麦克风。异常信息:' + (error.code || error.name))              break          }        })    } else {      HZRecorder.throwErr('当前浏览器不支持录音功能。'); return    }  }}export default HZRecorder

“Vue如何实现悬浮框自由移动+录音功能”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注编程网网站,小编将为大家输出更多高质量的实用文章!

--结束END--

本文标题: Vue如何实现悬浮框自由移动+录音功能

本文链接: https://www.lsjlt.com/news/343119.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Vue如何实现悬浮框自由移动+录音功能
    本篇内容介绍了“Vue如何实现悬浮框自由移动+录音功能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!效果如下主要功能一个漂浮的球,在全屏幕中...
    99+
    2023-07-02
  • Vue实现悬浮框自由移动+录音功能的示例代码
    目录效果如下主要功能实现1.封装第一个漂浮组件FloatBall.vue2.封装第二个组件录音组件Audio.vue3.recorder.js效果如下 主要功能 1.一个漂浮的球...
    99+
    2022-11-13
  • vue实现带自动吸附功能的悬浮球
    本文实例为大家分享了vue实现带自动吸附功能的悬浮球,供大家参考,具体内容如下 封装的组件代码,可以引到页面直接使用 <template>   <div     ...
    99+
    2022-11-13
  • vue如何实现移动端拖拽悬浮按钮
    这篇文章主要讲解了“vue如何实现移动端拖拽悬浮按钮”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue如何实现移动端拖拽悬浮按钮”吧!功能介绍:在移动端开发中,实现悬浮按钮在侧边显示,为不...
    99+
    2023-07-02
  • Android6.0如何实现双向通话自动录音功能
    这篇文章主要为大家展示了“Android6.0如何实现双向通话自动录音功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Android6.0如何实现双向通话自动录音功能”这篇文章吧。具体如下:项...
    99+
    2023-05-31
  • 如何用vue代码实现表格缩略图悬浮放大功能
    这篇文章主要介绍了如何用vue代码实现表格缩略图悬浮放大功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何用vue代码实现表格缩略图悬浮放大功能文章都会有所收获,下面我们一起来看看吧。element-ui界...
    99+
    2023-07-04
  • vue中如何实现element-ui表格缩略图悬浮放大功能
    这篇文章给大家分享的是有关vue中如何实现element-ui表格缩略图悬浮放大功能的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。element-ui界面非常简洁和美观,提供的组...
    99+
    2022-10-19
  • 如何使用Vue全家桶实现移动端音乐功能
    今天小编给大家分享一下如何使用Vue全家桶实现移动端音乐功能的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。src/ 文件目录...
    99+
    2023-07-04
  • Vue如何实现自动触发功能
    目录实现自动触发功能自定义指令实现自动点击事件1.若是没有v-for循环2.若是有v-for循环实现自动触发功能 今天在项目中遇到一个问题,就是当页面加载的时候,我希望某个元素通过点...
    99+
    2022-11-13
  • java web如何实现自动登录功能
    这篇文章主要介绍java web如何实现自动登录功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!具体内容如下主要思路就是:当用户访问网站的首页时,浏览器端会先检擦浏览器中存在的cookie中是否又登录的用户的用户名...
    99+
    2023-05-31
    java web
  • JS如何实现两周内自动登录功能
    这篇文章将为大家详细讲解有关JS如何实现两周内自动登录功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。具体内容如下<!DOCTYPE html> ...
    99+
    2022-10-19
  • HTML5中如何实现背景音乐自动播放功能
    小编给大家分享一下HTML5中如何实现背景音乐自动播放功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!音乐的自动播放属性,这里...
    99+
    2022-10-19
  • HTML5中如何拖放API实现自动生成相框功能
    这篇文章主要介绍了HTML5中如何拖放API实现自动生成相框功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。实现功能: 将桌面图片拖入指定地方,生成相框和相关信息。相框需要...
    99+
    2023-06-09
  • 基于Vue如何实现移动端图片裁剪组件功能
    小编给大家分享一下基于Vue如何实现移动端图片裁剪组件功能,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!最近项目上要做一个车牌识别的功能。本来以为很简单,只需要将图片扔给后台就可以了,但是...
    99+
    2022-10-19
  • vue2.0如何实现移动端的输入框实时检索更新列表功能
    这篇文章将为大家详细讲解有关vue2.0如何实现移动端的输入框实时检索更新列表功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。效果图html  &l...
    99+
    2022-10-19
  • shell脚本如何通过expect实现自动单边无密登录功能
    这篇文章主要介绍了shell脚本如何通过expect实现自动单边无密登录功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。代码如下:EXPECT=/usr/bin/expec...
    99+
    2023-06-09
  • Python如何实现获取网页内容及自动填表单与登录功能
    本文小编为大家详细介绍“Python如何实现获取网页内容及自动填表单与登录功能”,内容详细,步骤清晰,细节处理妥当,希望这篇“Python如何实现获取网页内容及自动填表单与登录功能”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来...
    99+
    2023-07-06
  • 移动端底部导航固定配合vue-router如何实现组件切换功能
    这篇文章主要介绍移动端底部导航固定配合vue-router如何实现组件切换功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!在我们平时练习或者实际项目中也好,我们常常遇到这么一个需求...
    99+
    2022-10-19
  • 微信小程序如何实现自定义弹出模态框禁止底部滚动功能
    小编给大家分享一下微信小程序如何实现自定义弹出模态框禁止底部滚动功能,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!图示:wxml代码:<view class='fi...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作