鴻蒙OSUniApp 實現的語音輸入與語音識別功能#三方框架 #Uniapp

UniApp 實現的語音輸入與語音識別功能

最近在開發跨平臺應用時,客戶要求添加語音輸入功能以提升用戶體驗。經過一番調研和實踐,我成功在UniApp項目中實現了語音輸入與識別功能,現將過程和方法分享出來,希望對有類似需求的開發者有所幫助。

為什么需要語音輸入功能?

隨著移動設備的普及,語音交互已成為一種高效的人機交流方式。與傳統的文字輸入相比,語音輸入具有以下優勢:

  1. 操作便捷:免去鍵盤敲擊,尤其適合單手操作或行走等場景
  2. 輸入高效:語音輸入速度通常快于手動輸入
  3. 提升體驗:為特定人群(如老年人、視障人士)提供便利
  4. 解放雙手:適用于駕車、做家務等無法騰出手打字的場景

在商業應用中,語音輸入可以顯著降低用戶的操作門檻,提高轉化率和用戶留存。

技術方案選型

在UniApp環境中實現語音識別,主要有三種方案:

  1. 使用原生插件:調用各平臺的原生語音識別能力
  2. 對接云服務:接入第三方語音識別API(如百度、訊飛等)
  3. Web API:在H5平臺利用Web Speech API

經過對比和測試,我最終采用了混合方案:

  • 在App平臺使用原生插件獲取最佳體驗
  • 在微信小程序使用微信自帶的語音識別能力
  • 在H5平臺嘗試使用Web Speech API,不支持時降級為云服務API

實現步驟

1. App端實現(基于原生插件)

首先需要安裝語音識別插件。我選擇了市場上比較成熟的speech-baidu插件,這是基于百度語音識別SDK封裝的UniApp插件。

安裝插件后,在manifest.json中配置:

"app-plus": {"plugins": {"speech": {"baidu": {"appid": "你的百度語音識別AppID","apikey": "你的API Key","secretkey": "你的Secret Key"}}},"distribute": {"android": {"permissions": ["<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>","<uses-permission android:name=\"android.permission.INTERNET\"/>"]}}
}

接下來創建語音識別組件:

<template><view class="voice-input-container"><view class="voice-btn" :class="{ 'recording': isRecording }"@touchstart="startRecord" @touchend="stopRecord"@touchcancel="cancelRecord"><image :src="isRecording ? '/static/mic-active.png' : '/static/mic.png'" mode="aspectFit"></image><text>{{ isRecording ? '松開結束' : '按住說話' }}</text></view><view v-if="isRecording" class="recording-tip"><text>正在聆聽...</text><view class="wave-container"><view v-for="(item, index) in waveItems" :key="index" class="wave-item":style="{ height: item + 'rpx' }"></view></view></view></view>
</template><script>
// #ifdef APP-PLUS
const speechPlugin = uni.requireNativePlugin('speech-baidu');
// #endifexport default {name: 'VoiceInput',data() {return {isRecording: false,timer: null,waveItems: [10, 15, 20, 25, 30, 25, 20, 15, 10]}},props: {lang: {type: String,default: 'zh'  // zh: 中文, en: 英文},maxDuration: {type: Number,default: 60  // 最長錄音時間,單位秒}},methods: {startRecord() {if (this.isRecording) return;// 申請錄音權限uni.authorize({scope: 'scope.record',success: () => {this.isRecording = true;this.startWaveAnimation();// #ifdef APP-PLUSspeechPlugin.start({vadEos: 3000,  // 靜音超時時間language: this.lang === 'zh' ? 'zh-cn' : 'en-us'}, (res) => {if (res.errorCode === 0) {// 識別結果this.$emit('result', res.result);} else {uni.showToast({title: `識別失敗: ${res.errorCode}`,icon: 'none'});}this.isRecording = false;this.stopWaveAnimation();});// #endif// 設置最長錄制時間this.timer = setTimeout(() => {if (this.isRecording) {this.stopRecord();}}, this.maxDuration * 1000);},fail: () => {uni.showToast({title: '請授權錄音權限',icon: 'none'});}});},stopRecord() {if (!this.isRecording) return;// #ifdef APP-PLUSspeechPlugin.stop();// #endifclearTimeout(this.timer);this.isRecording = false;this.stopWaveAnimation();},cancelRecord() {if (!this.isRecording) return;// #ifdef APP-PLUSspeechPlugin.cancel();// #endifclearTimeout(this.timer);this.isRecording = false;this.stopWaveAnimation();},// 波形動畫startWaveAnimation() {this.waveAnimTimer = setInterval(() => {this.waveItems = this.waveItems.map(() => Math.floor(Math.random() * 40) + 10);}, 200);},stopWaveAnimation() {clearInterval(this.waveAnimTimer);this.waveItems = [10, 15, 20, 25, 30, 25, 20, 15, 10];}},beforeDestroy() {this.cancelRecord();}
}
</script><style scoped>
.voice-input-container {width: 100%;
}.voice-btn {width: 200rpx;height: 200rpx;border-radius: 100rpx;background-color: #f5f5f5;display: flex;flex-direction: column;align-items: center;justify-content: center;margin: 0 auto;
}.voice-btn.recording {background-color: #e1f5fe;box-shadow: 0 0 20rpx rgba(0, 120, 255, 0.5);
}.voice-btn image {width: 80rpx;height: 80rpx;margin-bottom: 10rpx;
}.recording-tip {margin-top: 30rpx;text-align: center;
}.wave-container {display: flex;justify-content: center;align-items: flex-end;height: 80rpx;margin-top: 20rpx;
}.wave-item {width: 8rpx;background-color: #1890ff;margin: 0 5rpx;border-radius: 4rpx;transition: height 0.2s;
}
</style>

2. 微信小程序實現

微信小程序提供了原生的語音識別API,使用非常方便:

// 在小程序環境下的代碼
startRecord() {// #ifdef MP-WEIXINthis.isRecording = true;this.startWaveAnimation();const recorderManager = wx.getRecorderManager();recorderManager.onStart(() => {console.log('錄音開始');});recorderManager.onStop((res) => {this.isRecording = false;this.stopWaveAnimation();// 將錄音文件發送到微信后臺識別wx.showLoading({ title: '識別中...' });const { tempFilePath } = res;wx.uploadFile({url: 'https://api.weixin.qq.com/cgi-bin/media/voice/translatecontent',filePath: tempFilePath,name: 'media',formData: {access_token: this.accessToken,format: 'mp3',voice_id: Date.now(),lfrom: this.lang === 'zh' ? 'zh_CN' : 'en_US',lto: 'zh_CN'},success: (uploadRes) => {wx.hideLoading();const data = JSON.parse(uploadRes.data);if (data.errcode === 0) {this.$emit('result', data.result);} else {uni.showToast({title: `識別失敗: ${data.errmsg}`,icon: 'none'});}},fail: () => {wx.hideLoading();uni.showToast({title: '語音識別失敗',icon: 'none'});}});});recorderManager.start({duration: this.maxDuration * 1000,sampleRate: 16000,numberOfChannels: 1,encodeBitRate: 48000,format: 'mp3'});// #endif
},stopRecord() {// #ifdef MP-WEIXINwx.getRecorderManager().stop();// #endif// ...與App端相同的代碼...
}

需要注意的是,微信小程序的語音識別需要獲取access_token,這通常需要在后端實現并提供接口。

3. H5端實現

在H5端,我們可以利用Web Speech API來實現語音識別,當瀏覽器不支持時則降級為云服務API:

startRecord() {// #ifdef H5this.isRecording = true;this.startWaveAnimation();// 檢查瀏覽器是否支持Speech Recognitionif ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;this.recognition = new SpeechRecognition();this.recognition.lang = this.lang === 'zh' ? 'zh-CN' : 'en-US';this.recognition.continuous = false;this.recognition.interimResults = false;this.recognition.onresult = (event) => {const result = event.results[0][0].transcript;this.$emit('result', result);};this.recognition.onerror = (event) => {uni.showToast({title: `識別錯誤: ${event.error}`,icon: 'none'});};this.recognition.onend = () => {this.isRecording = false;this.stopWaveAnimation();};this.recognition.start();} else {// 不支持Web Speech API,調用云服務APIthis.useCloudSpeechAPI();}// #endif// 設置最長錄制時間this.timer = setTimeout(() => {if (this.isRecording) {this.stopRecord();}}, this.maxDuration * 1000);
},stopRecord() {// #ifdef H5if (this.recognition) {this.recognition.stop();}// #endif// ...與App端相同的代碼...
},useCloudSpeechAPI() {// 這里實現降級方案,調用后端接口進行語音識別uni.chooseFile({count: 1,type: 'file',extension: ['.mp3', '.wav'],success: (res) => {const tempFilePath = res.tempFilePaths[0];// 上傳音頻文件到后端進行識別uni.uploadFile({url: this.apiBaseUrl + '/speech/recognize',filePath: tempFilePath,name: 'audio',formData: {lang: this.lang},success: (uploadRes) => {const data = JSON.parse(uploadRes.data);if (data.code === 0) {this.$emit('result', data.result);} else {uni.showToast({title: `識別失敗: ${data.msg}`,icon: 'none'});}},complete: () => {this.isRecording = false;this.stopWaveAnimation();}});}});
}

4. 通用接口封裝

為了讓調用方便,我封裝了一個統一的API:

// 在 utils/speech.js 中
const Speech = {// 開始語音識別startRecognize(options) {const { lang = 'zh', success, fail, complete } = options;// #ifdef APP-PLUSconst speechPlugin = uni.requireNativePlugin('speech-baidu');speechPlugin.start({vadEos: 3000,language: lang === 'zh' ? 'zh-cn' : 'en-us'}, (res) => {if (res.errorCode === 0) {success && success(res.result);} else {fail && fail(res);}complete && complete();});return {stop: () => speechPlugin.stop(),cancel: () => speechPlugin.cancel()};// #endif// #ifdef MP-WEIXIN// 微信小程序實現邏輯// ...// #endif// #ifdef H5// H5實現邏輯// ...// #endif}
};export default Speech;

實戰案例:聊天應用中的語音輸入

現在,我們來看一個實際應用場景 - 在聊天應用中添加語音輸入功能:

<template><view class="chat-input-container"><view class="chat-tools"><image :src="isVoiceMode ? '/static/keyboard.png' : '/static/mic.png'" @tap="toggleInputMode"></image><image src="/static/emoji.png" @tap="showEmojiPicker"></image></view><view v-if="!isVoiceMode" class="text-input"><textareav-model="message"auto-heightplaceholder="請輸入消息...":focus="textFocus"@focus="onFocus"@blur="onBlur"></textarea></view><view v-else class="voice-input"><voice-input @result="onVoiceResult"></voice-input></view><button class="send-btn" :disabled="!message.trim()" @tap="sendMessage">發送</button></view>
</template><script>
import VoiceInput from '@/components/voice-input/voice-input.vue';export default {components: {VoiceInput},data() {return {message: '',isVoiceMode: false,textFocus: false};},methods: {toggleInputMode() {this.isVoiceMode = !this.isVoiceMode;if (!this.isVoiceMode) {this.$nextTick(() => {this.textFocus = true;});}},onVoiceResult(result) {this.message = result;this.isVoiceMode = false;},sendMessage() {if (!this.message.trim()) return;this.$emit('send', this.message);this.message = '';},onFocus() {this.textFocus = true;},onBlur() {this.textFocus = false;},showEmojiPicker() {// 顯示表情選擇器}}
};
</script><style>
.chat-input-container {display: flex;align-items: center;padding: 20rpx;border-top: 1rpx solid #eee;background-color: #fff;
}.chat-tools {display: flex;margin-right: 20rpx;
}.chat-tools image {width: 60rpx;height: 60rpx;margin-right: 20rpx;
}.text-input {flex: 1;background-color: #f5f5f5;border-radius: 10rpx;padding: 10rpx 20rpx;
}.text-input textarea {width: 100%;min-height: 60rpx;max-height: 240rpx;
}.voice-input {flex: 1;display: flex;justify-content: center;
}.send-btn {width: 140rpx;height: 80rpx;line-height: 80rpx;font-size: 28rpx;margin-left: 20rpx;padding: 0;background-color: #1890ff;color: #fff;
}.send-btn[disabled] {background-color: #ccc;
}
</style>

性能優化和注意事項

在實際開發中,我遇到了一些需要特別注意的問題:

1. 權限處理

語音識別需要麥克風權限,不同平臺的權限處理方式不同:

// 統一請求錄音權限
requestAudioPermission() {return new Promise((resolve, reject) => {// #ifdef APP-PLUSconst permissions = ['android.permission.RECORD_AUDIO'];plus.android.requestPermissions(permissions,function(e) {if (e.granted.length === permissions.length) {resolve();} else {reject(new Error('未授予錄音權限'));}},function(e) {reject(e);});// #endif// #ifdef MP-WEIXIN || MP-BAIDUuni.authorize({scope: 'scope.record',success: () => resolve(),fail: (err) => reject(err)});// #endif// #ifdef H5if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {navigator.mediaDevices.getUserMedia({ audio: true }).then(() => resolve()).catch(err => reject(err));} else {reject(new Error('瀏覽器不支持錄音功能'));}// #endif});
}

2. 流量控制

語音識別需要上傳音頻數據,在移動網絡下會消耗流量:

// 檢查網絡環境并提示用戶
checkNetwork() {uni.getNetworkType({success: (res) => {if (res.networkType === '2g' || res.networkType === '3g') {uni.showModal({title: '流量提醒',content: '當前處于移動網絡環境,語音識別可能消耗較多流量,是否繼續?',success: (confirm) => {if (confirm.confirm) {this.startSpeechRecognition();}}});} else {this.startSpeechRecognition();}}});
}

3. 性能優化

長時間語音識別會增加內存和電量消耗,需要做好優化:

// 設置最大錄音時長和自動結束
setupMaxDuration() {if (this.timer) {clearTimeout(this.timer);}this.timer = setTimeout(() => {if (this.isRecording) {uni.showToast({title: '錄音時間過長,已自動結束',icon: 'none'});this.stopRecord();}}, this.maxDuration * 1000);
}// 空閑自動停止
setupVAD() {// 監測靜音,如果用戶停止說話3秒,自動結束錄音let lastAudioLevel = 0;let silenceCounter = 0;this.vadTimer = setInterval(() => {// 獲取當前音量const currentLevel = this.getAudioLevel();if (Math.abs(currentLevel - lastAudioLevel) < 0.05) {silenceCounter++;if (silenceCounter > 30) { // 3秒 (30 * 100ms)this.stopRecord();}} else {silenceCounter = 0;}lastAudioLevel = currentLevel;}, 100);
}

增強功能:語音合成(TTS)

除了語音識別外,語音合成(Text-to-Speech)也是很有用的功能,可以將文本轉換為語音:

// 語音合成
textToSpeech(text, options = {}) {const { lang = 'zh', speed = 5, volume = 5 } = options;// #ifdef APP-PLUSconst speechPlugin = uni.requireNativePlugin('speech-baidu');return new Promise((resolve, reject) => {speechPlugin.textToSpeech({text,language: lang === 'zh' ? 'zh-cn' : 'en-us',speed,volume}, (res) => {if (res.errorCode === 0) {resolve(res);} else {reject(new Error(`語音合成失敗: ${res.errorCode}`));}});});// #endif// #ifdef H5return new Promise((resolve, reject) => {if ('speechSynthesis' in window) {const speech = new SpeechSynthesisUtterance();speech.text = text;speech.lang = lang === 'zh' ? 'zh-CN' : 'en-US';speech.rate = speed / 10;speech.volume = volume / 10;speech.onend = () => {resolve();};speech.onerror = (err) => {reject(err);};window.speechSynthesis.speak(speech);} else {reject(new Error('當前瀏覽器不支持語音合成'));}});// #endif
}

踩坑記錄與解決方案

開發過程中,我遇到了一些常見問題與解決方法,分享如下:

  1. 百度語音插件初始化失敗:檢查API密鑰配置和網絡環境,特別是HTTPS限制
  2. H5錄音無法使用:多數瀏覽器要求必須在HTTPS環境下才能使用麥克風
  3. 識別結果不準確:嘗試調整錄音參數,如采樣率、聲道數等,或者使用更專業的噪聲抑制算法
  4. 微信小程序調用失敗:檢查access_token是否有效,注意token有效期
  5. 不同設備體驗差異大:針對低端設備優化,如減少動畫效果、降低采樣率等

我們的解決方案是進行兼容性檢測,并根據設備性能自動調整參數:

// 檢測設備性能并調整參數
detectDevicePerformance() {const platform = uni.getSystemInfoSync().platform;const brand = uni.getSystemInfoSync().brand;const model = uni.getSystemInfoSync().model;// 低端安卓設備優化if (platform === 'android') {// 特定型號的優化if (brand === 'samsung' && model.includes('SM-J')) {return {sampleRate: 8000,quality: 'low',useVAD: false // 禁用語音活動檢測,降低CPU占用};}}// 默認配置return {sampleRate: 16000,quality: 'high',useVAD: true};
}

總結與展望

通過本文,我們探討了在UniApp中實現語音輸入與識別功能的多種方案,并提供了具體的代碼實現。這些實現方案已在實際項目中得到驗證,能夠滿足大多數應用場景的需求。

語音技術在移動應用中的重要性不斷提升,未來可以探索更多高級功能:

  1. 離線語音識別:降低網絡依賴,提高響應速度
  2. 多語言支持:增加更多語言的識別能力
  3. 聲紋識別:通過語音實現用戶身份驗證
  4. 情感分析:從語音中識別用戶情緒

希望本文對你在UniApp中實現語音功能有所幫助!如有問題歡迎在評論區交流討論。

參考資料

  1. UniApp官方文檔
  2. 百度語音識別API文檔
  3. Web Speech API

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80551.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80551.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80551.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2025年衛星遙感行業最新發展趨勢深度分析

一、國內發展趨勢&#xff1a;政策引領與技術突破雙輪驅動 &#xff08;一&#xff09;政策體系持續完善&#xff0c;頂層設計深化行業發展 國家級戰略與標準體系構建 中國政府將衛星遙感產業納入“十四五”規劃核心戰略&#xff0c;明確構建“通導遙”一體化空間基礎設施。20…

SIP協議棧--osip源碼梳理

文章目錄 osiposip主體結構體code main函數 狀態機轉化結構體code狀態轉換 sip事務結構體code osip_dialog結構體code 創建并發送200 OK響應 osip_message結構體code osip_eventcode 打印接收到的SIP消息 osip OSIP&#xff08;Open Source Implementation of SIP&#xff09;…

Linux之Yum源與Nginx服務篇

1.Yum源知識理論總結概括 Yum源概述 Yum 源 即軟件倉庫的標識&#xff0c;里面承載著軟件包集合 Yum源組成 包含模塊 【OS】、【everything】、【EPOL】、【debuginfo】、【source】、【update-source】 【os】:簡稱operator system 它內部包含操作系統的核心組件&#x…

從單體架構到微服務:架構演進之路

引言&#xff1a;當“大貨車”遇上“集裝箱運輸” 在軟件開發領域&#xff0c;單體架構曾像一輛載滿貨物的大貨車&#xff0c;將所有功能打包在一個應用中。但隨著業務復雜度飆升&#xff0c;這輛“大貨車”逐漸陷入泥潭&#xff1a;啟動慢如蝸牛、故障波及全局、升級如履薄冰……

AM32電調學習解讀九:ESC上電啟動關閉全流程波形分析

這是第九篇&#xff0c;前面的文章把各個模塊的實現都介紹了一輪&#xff0c;本章是從運行的角度結合波形圖&#xff0c;把整個流程走一遍。 先看下一運行的配置&#xff0c;我把一些配置關閉了&#xff0c;這樣跑起來會好分析一些&#xff0c;不同配置跑起來效果會有差異。使用…

全球寵物經濟新周期下的亞馬遜跨境采購策略革新——寵物用品賽道成本優化三維路徑

在全球"孤獨經濟"與"銀發經濟"雙輪驅動下&#xff0c;寵物用品市場正經歷結構性增長。Euromonitor數據顯示&#xff0c;2023年全球市場規模突破1520億美元&#xff0c;其中中國供應鏈貢獻度達38%&#xff0c;跨境電商出口增速連續三年超25%。在亞馬遜流量紅…

reshape/view/permute的原理

在pytorch中&#xff0c;Tensor的存儲是行主序的&#xff0c;也就是意味著最后一個維度的元素的存儲時連續的&#xff0c;reshape和view并不改變元素存儲的內存&#xff0c;僅僅改變訪問的間隔&#xff0c;下面舉例說明&#xff1b; 比如一個23的Tensor在內存中的存儲是連續的&…

upload-labs靶場通關詳解:第11關

一、分析源代碼 $is_upload false; $msg null; if (isset($_POST[submit])) {if (file_exists(UPLOAD_PATH)) {$deny_ext array("php","php5","php4","php3","php2","html","htm","phtml"…

L1-7 最短字母串【保姆級詳細講解】

請你設計一個程序&#xff0c;該程序接受起始字母和目標字母作為輸入&#xff0c;通過在字母表中向前或向后移動來計算兩個給定字母之間的最短路徑。然后&#xff0c;程序會沿著最短路徑打印出從起始字母到目標字母的所有字母。例如&#xff0c;如果輸入“c”和“k”作為起始字…

項目QT+ffmpeg+rtsp(三)——延遲巨低的項目+雙屏顯示

文章目錄 前言雙屏顯示widget.cppwidget.h前言 對于復現情況,分為兩種情況 第一種,對于我而言,是直接解壓后,就能直接運行了 第二種,對于師兄而言,需要你構建debug后,會產生這個文件夾,執行的時候,地址應該在這,我猜的,這里面沒有dll,exe程序就找不到dll這些庫,你…

ansible進階06

復雜的循環結構 循環基礎 [studentworktest myansible]$ cat users.yml --- - name: create usershosts: serveratasks:- name: create some usersuser:name: "{{item}}"password: "{{123456|password_hash(sha512)}}"state: presentloop:- zhangsan- li…

Go 模塊版本管理

Go 模塊版本管理指南 1、創建帶注釋的 Git 標簽 基本命令 # 創建帶注釋的標簽 git tag -a v1.0.0 -m "Release version 1.0.0 - initial stable release" -a&#xff1a;創建帶注釋的標簽 -m&#xff1a;添加標簽注釋信息 # 推送標簽到遠程倉庫 git push origin v…

Java—— IO流 第一期

什么是IO流 存儲和讀取數據的解決方案 I&#xff1a;input O&#xff1a;output 流&#xff1a;像水流一樣傳輸數據 IO流的作用 用于讀寫數據(本地文件&#xff0c;網絡) IO流的分類 按照流向分類 輸出流&#xff1a;程序 --> 文件 輸入流&#xff1a;文件 --> 程序 按照…

物聯網安全技術的最新進展與挑戰

隨著物聯網&#xff08;IoT&#xff09;技術的飛速發展&#xff0c;越來越多的設備被連接到互聯網&#xff0c;從智能家居設備到工業控制系統&#xff0c;物聯網正在深刻改變我們的生活和生產方式。然而&#xff0c;物聯網的安全問題也日益凸顯&#xff0c;成為制約其發展的關鍵…

【深度學習基礎】損失函數與優化算法詳解:從理論到實踐

【深度學習基礎】損失函數與優化算法詳解&#xff1a;從理論到實踐 一、引言 1. 損失函數與優化算法在深度學習中的核心作用 在深度學習中&#xff0c;模型訓練的本質是通過不斷調整參數&#xff0c;使模型輸出盡可能接近真實值。這一過程的核心驅動力是損失函數&#xff08;…

mvc-review

review&#xff1a; 1.Servlet生命周期中初始化方法&#xff1a;init(),init(config) public void init(ServletConfig config) throws ServletException { this.config config; this.init(); } 因此&#xff0c;如果我們需要…

YouTube視頻字幕轉成文章算重復內容嗎?

很多創作者誤以為「自己說的話不算抄襲」&#xff0c;卻不知道YouTube自動生成的字幕早已被搜索引擎存檔。 去年就有案例&#xff1a;某美食博主將教程視頻字幕轉為圖文&#xff0c;結果原創度檢測僅42%&#xff0c;導致頁面權重暴跌。 本文揭秘5個實操技巧&#xff1a;從刪除…

R語言數據可視化

R note book 文檔–輸出html格式文檔&#xff0c;plotly不能生成PDF文件 --- title: "R語言數據可視化" output: html_notebook ---在R語言中進行數據可視化是數據分析和呈現的重要環節&#xff0c;R提供了多種強大的繪圖系統和工具。以下是常見的數據可視化方法和示…

Axure難點解決分享:垂直菜單展開與收回(4大核心問題與專家級解決方案)

親愛的小伙伴,在您瀏覽之前,煩請關注一下,在此深表感謝!如有幫助請訂閱專欄! Axure產品經理精品視頻課已登錄CSDN可點擊學習https://edu.csdn.net/course/detail/40420 課程主題:垂直菜單展開與收回 主要內容:超長菜單實現、展開與收回bug解釋、Axure9版本限制等問題解…

云原生攻防2(Docker基礎補充)

Docker基礎入門 容器介紹 Docker是什么 Docker是基于Linux內核實現,最早是采用了 LXC技術,后來Docker自己研發了runc技術運行容器。 它基于Google Go語言實現,采用客戶端/服務端架構,使用API來管理和創建容器。 虛擬機 VS Docker Namespace 內核命名空間屬于容器非常核…