From 08f19a3414648f8b82103b7ad326b5bae46397a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=B8=E5=9B=BD=E5=AE=BE?= Date: Tue, 11 May 2021 17:55:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=A2=91=E8=B0=B1=E6=98=BE?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 84 +++++-- src/assets/Spectrum.js | 409 +++++++++++++++++++++++++++++++ src/components/SystemSetting.vue | 50 +++- 3 files changed, 516 insertions(+), 27 deletions(-) create mode 100644 src/assets/Spectrum.js diff --git a/src/App.vue b/src/App.vue index 45a899b..308926d 100755 --- a/src/App.vue +++ b/src/App.vue @@ -246,6 +246,7 @@ +
引用回复
@@ -397,6 +398,8 @@ import Login from './components/Login.vue'; + import Spectrum from './assets/Spectrum' + export default { components: { @@ -439,6 +442,8 @@ isEnableAtNotification: true, isEnableSendMessage: false, isEmojiBoxShow: false, + isSpectrumSwitch: false, + isSpectrumSwitchType: 0, messageList: [], // 消息最大允许保留 @@ -658,7 +663,7 @@ /** * @description: 获取URL参数 * @param {string} 参数名称 - * @return {string|null} + * @return {string|null} */ getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); @@ -716,6 +721,32 @@ } this.$forceUpdate(); }, + /** + * @description: 设置开启频谱 + * @param {bool} 是否开启频谱 + * @return {null} + */ + updateSpectrumSwitch(spectrumSwitch) { + this.$nextTick(() => { + if(spectrumSwitch){ + Spectrum.on(this.$refs.audio); + this.isSpectrumSwitchType = localStorage.getItem('isSpectrumSwitchType'); + this.updateSpectrumSwitchType(this.isSpectrumSwitchType) + }else{ + Spectrum.off(); + } + }) + }, + /** + * @description: 设置频谱类型 + * @param {int} 频谱类型 + * @return {null} + */ + updateSpectrumSwitchType(spectrumSwitchType) { + this.$nextTick(() => { + Spectrum.changeType(spectrumSwitchType); + }) + }, /** * @description: 读取本地配置 * @param {null} @@ -726,6 +757,8 @@ this.isEnableNotification = localStorage.getItem('isEnableNotification') != 1 ? true : false; this.isEnableTouchNotice = localStorage.getItem('isEnableTouchNotice') != 1 ? true : false; this.isEnableAtNotification = localStorage.getItem('isEnableAtNotification') != 1 ? true : false; + this.isSpectrumSwitch = localStorage.getItem('isSpectrumSwitch') != 1 ? true : false; + this.isSpectrumSwitchType = localStorage.getItem('isSpectrumSwitchType'); }, /** * @description: 显示右键菜单 @@ -1143,11 +1176,13 @@ }, /** * @description: 可以播放事件 开始播放 - * @param {null} - * @return {null} + * @param {null} + * @return {null} */ canplay() { this.$refs.audio.play(); + this.isSpectrumSwitch = localStorage.getItem('isSpectrumSwitch') != 1 ? true : false; + this.updateSpectrumSwitch(this.isSpectrumSwitch) }, /** * @description: 获取引用消息的标签 @@ -1643,7 +1678,7 @@ /** * @description: 输入框获取焦点 * @param {null} - * @return {null} + * @return {null} */ focusInput() { const textarea = document.querySelector(".bbug_main_chat_input_message"); @@ -1652,7 +1687,7 @@ /** * @description: at用户 * @param {null} - * @return {null} + * @return {null} */ atUser() { if (this.global.atUserInfo) { @@ -1664,7 +1699,7 @@ /** * @description: 摸一摸指定用户 * @param {int} 用户ID - * @return {null} + * @return {null} */ doTouch(user_id) { let that = this; @@ -1682,7 +1717,7 @@ /** * @description: 撤回消息 * @param {obj} 消息体 - * @return {null} + * @return {null} */ sendBackMessage(message) { let that = this; @@ -1697,7 +1732,7 @@ /** * @description: 引用消息 * @param {obj} 消息体 - * @return {null} + * @return {null} */ quotMessage(message) { let that = this; @@ -1713,7 +1748,7 @@ /** * @description: 用户头像下拉点击回调 * @param {obj} 命令对象 - * @return {null} + * @return {null} */ commandUserHead(cmd) { let that = this; @@ -1790,7 +1825,7 @@ /** * @description: 显示指定用户主页 * @param {int} 用户ID - * @return {null} + * @return {null} */ showUserPage(user_id) { let that = this; @@ -1804,7 +1839,7 @@ /** * @description: 显示在线列表 * @param {null} - * @return {null} + * @return {null} */ showOnlineList() { if (this.dialog.OnlineList) { @@ -1817,7 +1852,7 @@ /** * @description: 自动滚动到底部 * @param {null} - * @return {null} + * @return {null} */ autoScroll() { let that = this; @@ -1833,7 +1868,7 @@ /** * @description: 获取房间信息 * @param {bool} 是否重新连接(默认true) - * @return {null} + * @return {null} */ getRoomInfo(reConnect = true) { let that = this; @@ -1916,7 +1951,7 @@ /** * @description: 更新剪切板数据 * @param {null} - * @return {null} + * @return {null} */ updateCopyData() { let that = this; @@ -1933,7 +1968,7 @@ /** * @description: 切掉当前播放的歌曲 * @param {null} - * @return {null} + * @return {null} */ passTheSong() { let that = this; @@ -1957,7 +1992,7 @@ /** * @description: 收藏当前播放的歌曲 * @param {null} - * @return {null} + * @return {null} */ loveTheSong() { let that = this; @@ -1975,7 +2010,7 @@ /** * @description: 获取当前的websocket连接地址 * @param {null} - * @return {null} + * @return {null} */ getWebsocketUrl() { let that = this; @@ -2637,7 +2672,16 @@ overflow: hidden; overflow-y: scroll; padding-bottom: 30px; - z-index: 0; + z-index: 1; + } + + .bbbug_main_spectrum { + position: absolute; + left: 0; + right: 0; + height: 100px; + bottom: 150px; + z-index: 0; } .bbbug_main_chat_system { @@ -3061,4 +3105,4 @@ overflow: hidden; text-overflow: ellipsis; } - \ No newline at end of file + diff --git a/src/assets/Spectrum.js b/src/assets/Spectrum.js new file mode 100644 index 0000000..dd4061e --- /dev/null +++ b/src/assets/Spectrum.js @@ -0,0 +1,409 @@ +let Spectrum = { + type: 0, + audio: null, + audioCtx: null, + mediaElementSource: null, + analyser: null, + status: 1, //flag for sound is playing 1 or stopped 0 + animationId: null, + allCapsReachBottom: false, + $parentCont: null, + $cont: null, + $canvas: null, + _init: function (audio) { + try { + let userAgent = navigator.userAgent; + if (!/windows/i.test(userAgent) || (userAgent.indexOf('compatible') > -1 && userAgent.indexOf('MSIE') > -1)) { + return; + } + //下面这些是为了统一Chrome和Firefox的AudioContext + window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext; + window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame; + window.cancelAnimationFrame = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.msCancelAnimationFrame; + + let _this = this; + _this.audio = audio; + _this.$parentCont = document.getElementById('spectrumCont'); + if (!_this.$parentCont) { + return; + } + _this.audio.crossOrigin = 'anonymous'; + + _this.audioCtx = new window.AudioContext(); + + _this.audio.addEventListener('play', (e) => { + _this.on(); + }); + _this.audio.addEventListener('pause', (e) => { + _this.off(); + }); + + _this.audio.addEventListener('timeupdate', (e) => { + if (!_this.mediaElementSource) { + _this.on(); + } + }); + + function onResizeFunction() { + _this.on(); + } + + document.body.removeEventListener("resize", onResizeFunction); + document.body.addEventListener("resize", onResizeFunction); + window.removeEventListener("resize", onResizeFunction); + window.addEventListener("resize", onResizeFunction); + + } catch (e) { + console.error(e); + } + }, + on: function (audio) { + let _this = this; + if (_this.audioCtx && _this.audioCtx.state === 'running') { + if (!_this.mediaElementSource) { + _this.mediaElementSource = _this.audioCtx.createMediaElementSource(_this.audio); + _this.analyser = _this.audioCtx.createAnalyser(); + _this.mediaElementSource.connect(_this.analyser); + _this.analyser.connect(_this.audioCtx.destination); + } + _this.status = 1; + _this._render(_this.type); + } else if (audio) { + _this._init(audio); + } else if (_this.audio) { + _this._init(_this.audio); + } else { + _this.off(); + } + }, + off: function () { + let _this = this; + if (_this.audioCtx && _this.audioCtx.state === 'running') { + let _this = this; + // _this.analyser && _this.analyser.disconnect(_this.audioCtx.destination); + _this.status = 0; + } + }, + changeType(type) { + if (isNaN(type)) { + return; + } + type = type * 1; + let _this = this; + if (_this.audioCtx && _this.audioCtx.state === 'running') { + cancelAnimationFrame(_this.animationId); //since the sound is stoped and animation finished, stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT! + _this.$cont && _this.$cont.remove(); + _this._render(type); + } + }, + _render: function (type) { + let _this = this; + var refContWidth = _this.$parentCont.clientWidth; + var refContHeight = _this.$parentCont.clientHeight; + + let setting = { + width: refContWidth, + height: refContHeight * 1, + top: refContHeight * 1 + }; + + _this.$cont && _this.$cont.remove(); + + _this.$cont = document.createElement('div'); + _this.$cont.style.width = setting.width; + _this.$cont.style.height = setting.height; + _this.$cont.style.position = 'absolute'; + _this.$cont.style.top = setting.top; + _this.$cont.style.zIndex = 99; + + _this.$canvas = document.createElement('canvas'); + _this.$canvas.width = setting.width; + _this.$canvas.style.width = setting.width; + _this.$canvas.height = setting.height; + _this.$canvas.style.height = setting.height; + + _this.$cont.append(_this.$canvas); + _this.$parentCont.append(_this.$cont); + + + function contClickFunction() { + _this.changeType(_this.type + 1); + } + + _this.$cont.removeEventListener('click', contClickFunction) + _this.$cont.addEventListener('click', contClickFunction) + + let fftSize = 0; + let cavWidth = _this.$canvas.width; + for (let i = 3; i < 16; i++) { + fftSize = Math.pow(2, i); + if (cavWidth < fftSize) { + break; + } + } + if (!_this.analyser) { + return; + } + _this.analyser.fftSize = fftSize; + + // let bufferLength = analyser.frequencyBinCount; + // let dataArray = new Uint8Array(bufferLength); + + _this._draw(type); + }, + _draw: function (type) { + let _this = this; + //颜色数组 + let colorArray = ['#f82466', '#00FFFF', '#AFFF7C', '#FFAA6A', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A']; + //颜色随机数 + let colorRandom = Math.floor(Math.random() * colorArray.length); + //效果随机数 + let effectRandom = Math.floor(Math.random() * 1); + //随机选取颜色 + let color = colorArray[colorRandom]; + //随机选取效果 + switch (type) { + case 0: + //条形 + _this.type = type; + _this._draw_bar(color); + break; + case 1: + _this.type = type; + _this._draw_bar2(color); + break; + case 2: + _this.type = type; + _this._draw_line(color); + break; + default: + //条形 + _this.type = 0; + _this._draw_bar(color); + break; + } + }, + _draw_line: function (color) { + let _this = this; + let analyser = _this.analyser; + let canvas = _this.$canvas; + let width = canvas.width; + let height = canvas.height; + let canvasCtx = canvas.getContext('2d'); + + const bufferLength = this.analyser.fftSize; // 默认为2048 + const dataArray = new Uint8Array(bufferLength); + + let _draw_meter = function () { + analyser.getByteTimeDomainData(dataArray);// 将音频信息存储在长度为2048(默认)的类型数组(dataArray) + + if (_this.status === 0) { + //fix when some sounds end the value still not back to zero + for (let i = dataArray.length - 1; i >= 0; i--) { + dataArray[i] = 0; + } + _this.allCapsReachBottom = true; + for (let i = dataArray.length - 1; i >= 0; i--) { + _this.allCapsReachBottom = _this.allCapsReachBottom && (dataArray[i] === 0); + } + if (_this.allCapsReachBottom) { + cancelAnimationFrame(_this.animationId); //since the sound is stoped and animation finished, stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT! + _this.$cont && _this.$cont.remove(); + return; + } + } + + canvasCtx.clearRect(0, 0, width, height); + // canvasCtx.fillStyle = 'rgba(255, 255, 255, 0)';//background color + // canvasCtx.fillRect(0, 0, width, height); + canvasCtx.lineWidth = 2; + //['#f82466', '#00FFFF', '#AFFF7C', '#ffaa6a', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A'] + canvasCtx.strokeStyle = '#6AD5FF';//colorArray[Math.floor(Math.random() * 10)];//'#dc143c'; + canvasCtx.beginPath(); + + const sliceWidth = Number(width) / bufferLength; + let x = 0; + canvasCtx.moveTo(x, height / 2); + for (let i = 0; i < bufferLength; i++) { + const v = dataArray[i] / 128.0; + const y = v * height / 2; + canvasCtx['lineTo'](x, y); + x += sliceWidth; + } + canvasCtx.lineTo(width, height / 2); + canvasCtx.stroke(); + + _this.animationId = window.requestAnimationFrame(_draw_meter); // 定时动画 + }; + + _this.animationId = window.requestAnimationFrame(_draw_meter); // 定时动画 + }, + _draw_bar: function (color) { + let _this = this; + + // let colorArray = ['#f82466', '#00FFFF', '#AFFF7C', '#ffaa6a', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A']; + + let analyser = _this.analyser; + let canvas = _this.$canvas; + let cwidth = canvas.width, + cheight = canvas.height; + + // console.log(cheight); + + analyser.fftSize = 1024; + var bufferLength = analyser.frequencyBinCount; + let dataArray = new Uint8Array(bufferLength); + let slowDownDataArray = []; + + let canvasCtx = canvas.getContext('2d'); + let linearGradient = canvasCtx.createLinearGradient(0, 0, 0, cheight); + linearGradient.addColorStop(1, 'rgb(175,255,124)'); + linearGradient.addColorStop(0.3, 'rgb(255,170,106,1)'); + linearGradient.addColorStop(0, 'rgb(248,36,102,1)'); + + canvasCtx.clearRect(0, 0, cwidth, cheight); + + let _draw_meter = function () { + + analyser.getByteFrequencyData(dataArray); + + if (_this.status === 0) { + //fix when some sounds end the value still not back to zero + for (let i = dataArray.length - 1; i >= 0; i--) { + dataArray[i] = 0; + } + _this.allCapsReachBottom = true; + for (let i = slowDownDataArray.length - 1; i >= 0; i--) { + _this.allCapsReachBottom = _this.allCapsReachBottom && (slowDownDataArray[i] === 0); + } + if (_this.allCapsReachBottom) { + cancelAnimationFrame(_this.animationId); //since the sound is stoped and animation finished, stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT! + _this.$cont && _this.$cont.remove(); + return; + } + } + + // let backgroundColor = '#292a2d';//document.body.style.backgroundColor || 'rgb(255,255,255)'; + // canvasCtx.fillStyle = 'rgb()'; + // canvasCtx.fillRect(0, 0, cwidth, cheight); + canvasCtx.clearRect(0, 0, cwidth, cheight); + + let barWidth = (cwidth / bufferLength) * 2.5; + let barHeight; + let x = 0; + + for (let i = 0; i < bufferLength; i++) { + + barHeight = cheight * Number.parseFloat(dataArray[i] / 255.00).toFixed(2); + + if (slowDownDataArray.length < Math.round(bufferLength)) { + slowDownDataArray.push(barHeight); + } + + //['#f82466', '#00FFFF', '#AFFF7C', '#ffaa6a', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A'] + canvasCtx.fillStyle = '#6AD5FF';//colorArray[Math.floor(Math.random() * 10)];//'#dc143c'; + //draw the cap, with transition effect + if (barHeight < slowDownDataArray[i]) { + canvasCtx.fillRect(x, cheight - (--slowDownDataArray[i]), barWidth, 2); + } else { + canvasCtx.fillRect(x, cheight - barHeight, barWidth, 2); + slowDownDataArray[i] = barHeight; + } + + canvasCtx.fillStyle = linearGradient; + canvasCtx.fillRect(x, cheight - barHeight + 4, barWidth, barHeight); + + x += barWidth + 1; + } + + _this.animationId = requestAnimationFrame(_draw_meter); + + }; + _this.animationId = requestAnimationFrame(_draw_meter); + + }, + _draw_bar2: function (color) { + let _this = this; + + // let colorArray = ['#f82466', '#00FFFF', '#AFFF7C', '#ffaa6a', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A']; + + let analyser = _this.analyser; + let canvas = _this.$canvas; + let cwidth = canvas.width, + cheight = canvas.height; + + // console.log(cheight); + + // analyser.fftSize = 256; + var bufferLength = analyser.frequencyBinCount; + let dataArray = new Uint8Array(bufferLength); + let slowDownDataArray = []; + + let canvasCtx = canvas.getContext('2d'); + let linearGradient = canvasCtx.createLinearGradient(0, 0, 0, cheight); + linearGradient.addColorStop(1, 'rgb(175,255,124)'); + linearGradient.addColorStop(0.3, 'rgb(255,170,106,1)'); + linearGradient.addColorStop(0, 'rgb(248,36,102,1)'); + + canvasCtx.clearRect(0, 0, cwidth, cheight); + + let _draw_meter = function () { + + analyser.getByteFrequencyData(dataArray); + + if (_this.status === 0) { + //fix when some sounds end the value still not back to zero + for (let i = dataArray.length - 1; i >= 0; i--) { + dataArray[i] = 0; + } + _this.allCapsReachBottom = true; + for (let i = slowDownDataArray.length - 1; i >= 0; i--) { + _this.allCapsReachBottom = _this.allCapsReachBottom && (slowDownDataArray[i] === 0); + } + if (_this.allCapsReachBottom) { + cancelAnimationFrame(_this.animationId); //since the sound is stoped and animation finished, stop the requestAnimation to prevent potential memory leak,THIS IS VERY IMPORTANT! + _this.$cont && _this.$cont.remove(); + return; + } + } + + // let backgroundColor = '#292a2d';//document.body.style.backgroundColor || 'rgb(255,255,255)'; + // canvasCtx.fillStyle = backgroundColor; + // canvasCtx.fillRect(0, 0, cwidth, cheight); + canvasCtx.clearRect(0, 0, cwidth, cheight); + + let barWidth = (cwidth / bufferLength) * 2.5; + let barHeight; + let x = 0; + + for (let i = 0; i < bufferLength; i++) { + barHeight = cheight * Number.parseFloat(dataArray[i] / 255.00).toFixed(2); + + if (slowDownDataArray.length < Math.round(bufferLength)) { + slowDownDataArray.push(barHeight); + } + + //['#f82466', '#00FFFF', '#AFFF7C', '#ffaa6a', '#6AD5FF', '#D26AFF', '#FF6AE6', '#FF6AB8', '#FF6A6A'] + canvasCtx.fillStyle = '#6AD5FF';//colorArray[Math.floor(Math.random() * 10)];//'#dc143c'; + //draw the cap, with transition effect + if (barHeight < slowDownDataArray[i]) { + canvasCtx.fillRect(x, cheight - (--slowDownDataArray[i]), barWidth, 2); + } else { + canvasCtx.fillRect(x, cheight - barHeight, barWidth, 2); + slowDownDataArray[i] = barHeight; + } + + canvasCtx.fillStyle = linearGradient; + canvasCtx.fillRect(x, cheight - barHeight + 4, barWidth, barHeight); + + x += barWidth - 1; + } + + _this.animationId = requestAnimationFrame(_draw_meter); + + }; + _this.animationId = requestAnimationFrame(_draw_meter); + + }, +}; +export default Spectrum; diff --git a/src/components/SystemSetting.vue b/src/components/SystemSetting.vue index 50dfa20..c0db1de 100755 --- a/src/components/SystemSetting.vue +++ b/src/components/SystemSetting.vue @@ -20,6 +20,16 @@ + + + + + + + + 线 + +
@@ -36,7 +46,9 @@ isEnableNotification: true, isEnableTouchNotice: true, isEnableAtNotification: true, - isDarkModelTemp: false + isDarkModelTemp: false, + isSpectrumSwitch: false, + isSpectrumSwitchType: 0 } }, created() { @@ -47,11 +59,13 @@ this.isEnableTouchNotice = localStorage.getItem('isEnableTouchNotice') != 1 ? true : false; this.isEnableAtNotification = localStorage.getItem('isEnableAtNotification') != 1 ? true : false; this.isDarkModelTemp = this.$parent.isDarkModel; + this.isSpectrumSwitch = localStorage.getItem('isSpectrumSwitch') != 1 ? true : false; + this.isSpectrumSwitchType = localStorage.getItem('isSpectrumSwitchType'); }, methods: { /** * @description: @通知切换事件 - * @param {null} + * @param {null} * @return {null} */ isEnableAtNotificationChanged() { @@ -60,7 +74,7 @@ }, /** * @description: 声音切换事件 - * @param {null} + * @param {null} * @return {null} */ isEnableNoticePlayerChanged() { @@ -69,7 +83,7 @@ }, /** * @description: 通知切换事件 - * @param {null} + * @param {null} * @return {null} */ isEnableNotificationChanged() { @@ -78,7 +92,7 @@ }, /** * @description: 摸一摸通知切换事件 - * @param {null} + * @param {null} * @return {null} */ isEnableTouchNoticeChanged() { @@ -87,11 +101,29 @@ }, /** * @description: 暗黑模式切换事件 - * @param {null} + * @param {null} * @return {null} */ isDarkModelChanged() { this.$parent.updateDarkModel(this.isDarkModelTemp); + }, + /** + * @description: 频谱开关切换事件 + * @param {null} + * @return {null} + */ + isSpectrumSwitchChanged() { + localStorage.setItem('isSpectrumSwitch', this.isSpectrumSwitch ? 0 : 1); + this.$parent.updateSpectrumSwitch(this.isSpectrumSwitch); + }, + /** + * @description: 频谱类型切换事件 + * @param {null} + * @return {null} + */ + isSpectrumSwitchTypeChanged(){ + localStorage.setItem('isSpectrumSwitchType', this.isSpectrumSwitchType); + this.$parent.updateSpectrumSwitchType(this.isSpectrumSwitchType); } }, } @@ -110,6 +142,10 @@ overflow-y: auto; } + .bbbug_my_setting__form_spectrum_switch .el-radio-button__inner{ + padding: 5px 10px; + } + .el-select { display: block; } @@ -135,4 +171,4 @@ .bbbug_my_setting__clear_button:active { background-color: red; } - \ No newline at end of file + -- Gitee