自定义音频播放样式结合Howler.js

滑动式滚动条

请添加图片描述

不使用audio默认样式  自定义音频播放样式
当前时间 开始时间 结束时间 可播放可暂停 滚动条可拖动进行同步
具体样式可调整
npm install howler --save
<template>
  <div class="audio-player">
    <div v-if="isLoading" class="loading-message">音频加载中...</div>
    <div v-else>
      <div class="audio-time-info">
        <span>开始时间: {{ formatDuration(startTime) }}</span>
        <span>当前时间: {{ formatDuration(currentTime) }}</span>
        <span>结束时间: {{ formatDuration(endTime) }}</span>
      </div>
      <div class="progress-container">
        <input
          type="range"
          class="progress-bar"
          :min="0"
          :max="duration"
          v-model.number="currentTime"
          @input="onSeek"/>
        <div class="progress-fill" :style="{ width: progressWidth }"></div>
      </div>
      <div class="control-buttons">
        <button @click="togglePlayPause">{{ playPauseButtonText }}</button>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
import {
  defineComponent,
  ref,
  onMounted,
  onBeforeUnmount,
  watch,
  computed
} from 'vue';
import { Howl, Howler } from 'howler';
function formatDuration(seconds: number): string {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);
  if (hours > 0) {
    return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  } else {
    return `${minutes}:${secs.toString().padStart(2, '0')}`;
  }
}
export default defineComponent({
  setup() {
    const audioSrc = ref('../20250109.wav'); // 请替换为实际的音频文件路径
    const sound = ref<Howl | null>(null);
    const currentTime = ref(0);
    const duration = ref(0);
    const startTime = ref(0);
    const endTime = ref(0);
    const playing = ref(false);
    const isLoading = ref(true);
    let requestId: number | null = null;

    const initAudio = () => {
      isLoading.value = true;
      sound.value = new Howl({
        src: [audioSrc.value],
        onload: () => {
          duration.value = sound.value!.duration();
          endTime.value = sound.value!.duration();
          isLoading.value = false;
        },
        onplay: () => {
          playing.value = true;
          startTime.value = sound.value!.seek();
          startUpdatingTime();
        },
        onpause: () => {
          playing.value = false;
          stopUpdatingTime();
        },
        onend: () => {
          playing.value = false;
          startTime.value = 0;
          currentTime.value = 0;
          stopUpdatingTime();
        }
      });
    };

    const togglePlayPause = () => {
      if (sound.value) {
        if (playing.value) {
          sound.value.pause();
        } else {
          sound.value.play();
        }
      }
    };

    const onSeek = () => {
      if (sound.value) {
        sound.value.seek(currentTime.value);
      }
    };

    const startUpdatingTime = () => {
      const update = () => {
        if (sound.value && playing.value) {
          currentTime.value = sound.value!.seek();
          requestId = requestAnimationFrame(update);
        }
      };
      requestId = requestAnimationFrame(update);
    };

    const stopUpdatingTime = () => {
      if (requestId) {
        cancelAnimationFrame(requestId);
        requestId = null;
      }
    };

    onMounted(() => {
      initAudio();
    });

    onBeforeUnmount(() => {
      if (sound.value) {
        sound.value.unload();
      }
    });

    const progressWidth = computed(() => {
      if (duration.value > 0) {
        const percentage = (currentTime.value / duration.value) * 100;
        return `${percentage}%`;
      }
      return '0%';
    });

    return {
      sound,
      currentTime,
      duration,
      startTime,
      endTime,
      playing,
      togglePlayPause,
      onSeek,
      playPauseButtonText: computed(() => playing.value ? '暂停' : '播放'),
      formatDuration,
      isLoading,
      progressWidth
    };
  }
});
</script>
<style scoped>
.audio-player {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f9f9f9;
}

.audio-time-info {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  width: 100%;
}

.audio-time-info span {
  font-size: 14px;
  color: #666;
}

/* Progress Container */
.progress-container {
  position: relative;
  width: 100%;
  height: 8px; /* 设置一个合适的高度 */
  background-color: #ddd;
  border-radius: 4px; /* 圆角边框 */
  overflow: hidden;
  cursor: pointer;
}

/* Progress Bar (Hidden Input) */
.progress-bar {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  outline: none;
  opacity: 0; /* 使输入不可见 */
  z-index: 2; /* 确保它覆盖在 .progress-fill 上 */
  cursor: pointer;
}

/* Webkit 浏览器的滑块样式 */
.progress-bar::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}

/* Firefox 浏览器的滑块样式 */
.progress-bar::-moz-range-thumb {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}

/* Progress Fill */
.progress-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background-color: #4CAF50;
  transition: width 0.2s linear;
  /* appearance: none; 改变元素默认风格 */
  /* background-color: #0074fd; */
  z-index: 1;
}

.control-buttons button {
  width:60px;
  margin:10px 0 0 0;
  padding: 8px;
  border: none;
  background-color: #4CAF50;
  color: white;
  cursor: pointer;
  border-radius: 5px;
  transition: background-color 0.3s;
}

.control-buttons button:hover {
  background-color: #45a049;
}

.loading-message {
  color: #999;
  text-align: center;
  margin-bottom: 10px;
}
</style>

跳跃式滚动条

请添加图片描述

不使用audio默认样式  自定义音频播放样式
当前时间 开始时间 结束时间 可播放可暂停 滚动条可拖动进行同步
具体样式可调整
npm install howler --save
<template>
<!-- 跳跃式进度 -->
  <div class="audio-player">
    <div v-if="isLoading" class="loading-message">音频加载中...</div>
    <div v-else>
      <div class="audio-time-info">
        <span>开始时间: {{ formatDuration(startTime) }}</span>
        <span>结束时间: {{ formatDuration(endTime) }}</span>
        <span>当前时间: {{ formatDuration(currentTime) }}</span>
      </div>
      <input
        type="range"
        class="progress-bar"
        :min="0"
        :max="duration"
        v-model.number="currentTime"
        @input="onSeek"/>
      <div class="control-buttons">
        <button @click="togglePlayPause">{{ playPauseButtonText }}</button>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import {
  defineComponent,
  ref,
  onMounted,
  onBeforeUnmount,
  watch,
  computed,
  onUnmounted
} from 'vue';
import { Howl, Howler } from 'howler';

function formatDuration(seconds: number): string {
  const hours = Math.floor(seconds / 3600);
  const minutes = Math.floor((seconds % 3600) / 60);
  const secs = Math.floor(seconds % 60);
  if (hours > 0) {
    return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  } else {
    return `${minutes}:${secs.toString().padStart(2, '0')}`;
  }
}

export default defineComponent({
  setup() {
    const audioSrc = ref('../20250109.wav'); // 请替换为实际的音频文件路径
    const sound = ref<Howl | null>(null);
    const currentTime = ref(0);
    const duration = ref(0);
    const startTime = ref(0);
    const endTime = ref(0);
    const playing = ref(false);
    const isLoading = ref(true);
    let requestId: number | null = null;

    const initAudio = () => {
      isLoading.value = true;
      sound.value = new Howl({
        src: [audioSrc.value],
        onload: () => {
          duration.value = sound.value!.duration();
          endTime.value = sound.value!.duration();
          isLoading.value = false;
        },
        onplay: () => {
          playing.value = true;
          startTime.value = sound.value!.seek();
          startUpdatingTime();
        },
        onpause: () => {
          playing.value = false;
          stopUpdatingTime();
        },
        onend: () => {
          playing.value = false;
          startTime.value = 0;
          currentTime.value = 0;
          stopUpdatingTime();
        }
      });
    };

    const togglePlayPause = () => {
      if (sound.value) {
        if (playing.value) {
          sound.value.pause();
        } else {
          sound.value.play();
        }
      }
    };

    const onSeek = () => {
      if (sound.value) {
        sound.value.seek(currentTime.value);
      }
    };

    const startUpdatingTime = () => {
      const update = () => {
        if (sound.value && playing.value) {
          currentTime.value = sound.value!.seek();
          requestId = requestAnimationFrame(update);
        }
      };
      requestId = requestAnimationFrame(update);
    };

    const stopUpdatingTime = () => {
      if (requestId) {
        cancelAnimationFrame(requestId);
        requestId = null;
      }
    };

    onMounted(() => {
      initAudio();
    });

    onBeforeUnmount(() => {
      if (sound.value) {
        sound.value.unload();
      }
    });

    onUnmounted(() => {
      stopUpdatingTime();
    });

    watch(currentTime, () => {
      if (sound.value) {
        sound.value.seek(currentTime.value);
      }
    });

    return {
      sound,
      currentTime,
      duration,
      startTime,
      endTime,
      playing,
      togglePlayPause,
      onSeek,
      playPauseButtonText: computed(() => (playing.value? '暂停' : '播放')),
      formatDuration,
      isLoading
    };
  }
});
</script>

<style scoped>
.audio-player {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f9f9f9;
}
.audio-time-info {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  width: 100%;
}
.audio-time-info span {
  font-size: 14px;
  color: #666;
}
.progress-bar {
  width: 100%;
  margin-bottom: 10px;
  -webkit-appearance: none;
  appearance: none;
  height: 5px;
  background: #ddd;
  outline: none;
  opacity: 0.7;
  -webkit-transition: 0.2s;
  transition: 0.2s;
  border-radius: 5px;
}
.progress-bar::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
}
.progress-bar::-moz-range-thumb {
  width: 15px;
  height: 15px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
}
.control-buttons button {
  padding: 10px 20px;
  border: none;
  background-color: #4CAF50;
  color: white;
  cursor: pointer;
  border-radius: 5px;
}
.loading-message {
  color: #999;
  text-align: center;
  margin-bottom: 10px;
}
</style>

自定义音频播放组件

如需更改可进行更改样式 

请添加图片描述

不使用audio默认样式  自定义音频播放样式
当前时间 开始时间 结束时间 可播放可暂停 滚动条可拖动进行同步
具体样式可调整
npm install howler --save

父组件

<template>
  <div>
    <AudioPlayer :src="'../20250109.wav'" @play="handlePlay" @pause="handlePause" @end="handleEnd" />
    // src对应你的音频文件
  </div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import AudioPlayer from './components/pop.vue';

export default defineComponent({
  components: {
    AudioPlayer
  },
  methods: {
    handlePlay() {
      console.log('音频开始播放');
    },
    handlePause() {
      console.log('音频暂停');
    },
    handleEnd() {
      console.log('音频播放结束');
    }
  }
});
</script>

子组件

components-pop.vue

<template>
  <div class="audio-player">
    <div v-if="isLoading" class="loading-message">音频加载中...</div>
    <div v-else>
      <div class="audio-time-info">
        <span>开始时间: {{ formatDuration(startTime) }}</span>
        <span>当前时间: {{ formatDuration(currentTime) }}</span>
        <span>结束时间: {{ formatDuration(endTime) }}</span>
      </div>
      <div class="progress-container">
        <input
          type="range"
          class="progress-bar"
          :min="0"
          :max="duration"
          v-model.number="currentTime"
          @input="onSeek"/>
        <div class="progress-fill" :style="{ width: progressWidth }"></div>
      </div>
      <div class="control-buttons">
        <button @click="togglePlayPause">{{ playPauseButtonText }}</button>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, PropType, ref, onMounted, onBeforeUnmount, watch, computed } from 'vue';
import { Howl, Howler } from 'howler';

export default defineComponent({
  name: 'AudioPlayer',
  props: {
    src: {
      type: String as PropType<string>,
      required: true,
    },
  },
  emits: ['play', 'pause', 'end'],
  setup(props, { emit }) {
    const sound = ref<Howl | null>(null);
    const currentTime = ref(0);
    const duration = ref(0);
    const startTime = ref(0);
    const endTime = ref(0);
    const playing = ref(false);
    const isLoading = ref(true);
    let requestId: number | null = null;

    const initAudio = () => {
      isLoading.value = true;
      sound.value = new Howl({
        src: [props.src],
        onload: () => {
          duration.value = sound.value!.duration();
          endTime.value = sound.value!.duration();
          isLoading.value = false;
        },
        onplay: () => {
          playing.value = true;
          startTime.value = sound.value!.seek();
          startUpdatingTime();
          emit('play');
        },
        onpause: () => {
          playing.value = false;
          stopUpdatingTime();
          emit('pause');
        },
        onend: () => {
          playing.value = false;
          startTime.value = 0;
          currentTime.value = 0;
          stopUpdatingTime();
          emit('end');
        }
      });
    };

    const togglePlayPause = () => {
      if (sound.value) {
        if (playing.value) {
          sound.value.pause();
        } else {
          sound.value.play();
        }
      }
    };

    const onSeek = () => {
      if (sound.value) {
        sound.value.seek(currentTime.value);
      }
    };

    const startUpdatingTime = () => {
      const update = () => {
        if (sound.value && playing.value) {
          currentTime.value = sound.value!.seek()!;
          requestId = requestAnimationFrame(update);
        }
      };
      requestId = requestAnimationFrame(update);
    };

    const stopUpdatingTime = () => {
      if (requestId) {
        cancelAnimationFrame(requestId);
        requestId = null;
      }
    };

    onMounted(() => {
      initAudio();
    });

    onBeforeUnmount(() => {
      if (sound.value) {
        sound.value.unload();
      }
    });

    const progressWidth = computed(() => {
      if (duration.value > 0) {
        const percentage = (currentTime.value / duration.value) * 100;
        return `${percentage}%`;
      }
      return '0%';
    });

    const formatDuration = (seconds: number): string => {
      const hours = Math.floor(seconds / 3600);
      const minutes = Math.floor((seconds % 3600) / 60);
      const secs = Math.floor(seconds % 60);
      if (hours > 0) {
        return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
      } else {
        return `${minutes}:${secs.toString().padStart(2, '0')}`;
      }
    };

    return {
      sound,
      currentTime,
      duration,
      startTime,
      endTime,
      playing,
      togglePlayPause,
      onSeek,
      playPauseButtonText: computed(() => playing.value ? '暂停' : '播放'),
      formatDuration,
      isLoading,
      progressWidth,
    };
  }
});
</script>

<style scoped>
/* 省略了之前的样式部分,因为它们已经符合要求 */
.audio-player {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px;
  border: 1px solid #ccc;
  border-radius: 5px;
  background-color: #f9f9f9;
}

.audio-time-info {
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  width: 100%;
}

.audio-time-info span {
  font-size: 14px;
  color: #666;
}

/* Progress Container */
.progress-container {
  position: relative;
  width: 100%;
  height: 8px; /* 设置一个合适的高度 */
  background-color: #ddd;
  border-radius: 4px; /* 圆角边框 */
  overflow: hidden;
  cursor: pointer;
}

/* Progress Bar (Hidden Input) */
.progress-bar {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  outline: none;
  opacity: 0; /* 使输入不可见 */
  z-index: 2; /* 确保它覆盖在 .progress-fill 上 */
  cursor: pointer;
}

/* Webkit 浏览器的滑块样式 */
.progress-bar::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}

/* Firefox 浏览器的滑块样式 */
.progress-bar::-moz-range-thumb {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: #4CAF50;
  cursor: pointer;
  box-shadow: 0 0 2px rgba(0, 0, 0, 0.5);
}

/* Progress Fill */
.progress-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background-color: #4CAF50;
  transition: width 0.2s linear;
  z-index: 1;
}

.control-buttons button {
  width:60px;
  margin:10px 0 0 0;
  padding: 8px;
  border: none;
  background-color: #4CAF50;
  color: white;
  cursor: pointer;
  border-radius: 5px;
  transition: background-color 0.3s;
}

.control-buttons button:hover {
  background-color: #45a049;
}

.loading-message {
  color: #999;
  text-align: center;
  margin-bottom: 10px;
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/951786.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于 GEE 利用 DEM 数据计算坡度、坡向

目录 1 完整代码 2 运行结果 1 完整代码 以SRTM数据产品为例&#xff0c;代码如下&#xff1a; var roi table; var srtm ee.Image(USGS/SRTMGL1_003); var elevation srtm.select(elevation).clip(roi);// 计算坡度 var slope ee.Terrain.slope(elevation).clip(roi)…

Ubuntu 磁盘修复

Ubuntu 磁盘修复 在 ubuntu 文件系统变成只读模式&#xff0c;该处理呢&#xff1f; 文件系统内部的错误&#xff0c;如索引错误、元数据损坏等&#xff0c;也可能导致系统进入只读状态。磁盘坏道或硬件故障也可能引发文件系统只读的问题。/etc/fstab配置错误&#xff0c;可能…

夯实前端基础之CSS篇

知识点概览 这一篇是讲CSS的&#xff0c;前面还有一篇总结HTML的哦~夯实前端基础之HTML篇&#xff0c;后面还会更新其他系列哦~ 一、必知必会 1. html中引入样式的几种方式&#xff1f; 内联样式&#xff08;html元素的style属性&#xff09;内部样式表&#xff08;style标签…

conda/pip基本常用命令理解与整理

最近配置了两轮pytorch环境&#xff0c;由于要频繁用到各种conda和pip命令&#xff0c;所以再此整理一下。 文章目录 前言&#xff1a;conda虚拟环境总结与解读Conda和pip的理解区别和联系命令格式 conda环境命令查看创建和删除导出与导入激活和退出 包管理命令安装和删除文件批…

协方差矩阵

协方差矩阵是一个对称矩阵&#xff0c;用来描述多个随机变量之间的协方差关系。协方差反映了两个随机变量如何共同变化的趋势&#xff0c;协方差矩阵将这种关系扩展到了多维数据。 1. 定义 假设有一个 n 维随机向量 &#xff0c;协方差矩阵 Σ 定义为&#xff1a; 其中&#…

6 分布式限流框架

限流的作用 在API对外互联网开放的情况下&#xff0c;是无法控制调用方的行为的。当遇到请求激增或者黑客攻击的情况下&#xff0c;会导致接口占用大量的服务器资源&#xff0c;使得接口响应效率的降低或者超时&#xff0c;更或者导致服务器宕机。 限流是指对应用服务进行限制…

LINUX 下 NODE 安装与配置

一、官网地址&#xff1a; &#xff08;中文网&#xff09;https://nodejs.cn/ &#xff08;英文网&#xff09;https://nodejs.org/en/ 二、下载安装包 2.1、下载地址&#xff1a;下载 | Node.js 中文网 https://nodejs.cn/download/ 2.2、使用 wget 命令下载到linux 服务器…

Qt重写webrtc的demo peerconnection

整个demo为&#xff1a; 可以选择多个编码方式&#xff1a; cmake_minimum_required(VERSION 3.5)project(untitled LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_INCLUDE_CURRENT_DIR ON)set(CMAKE_AUTOUIC ON) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON)set(CMA…

el-tabs中tabs过多造成form表单输入的时候卡顿

这里写自定义目录标题 tabs过多造成的输入卡顿解决方案方案一方案二 出现的并发问题解决方案 tabs过多造成的输入卡顿 描述&#xff1a;前端要写一个需求&#xff0c;大概有一百多个tab渲染过来&#xff0c;每个tab中都是一个表单&#xff0c;这个时候数据渲染任务加重&#x…

【python/html/js 鼠标点选/框选图片内容】

html代码 注意修改图片地址&#xff0c;坐标会保存为coordinates.json <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0&q…

Zustand selector 发生 infinate loops的原因以及解决

Zustand selector 发生 infinate loops 做zustand tutorial project的时候&#xff0c;使用选择器方法引入store&#xff0c;出现Maximum update depth exceeded,也就是组件一直重新渲染&#xff0c;改成直接使用store就没有不会出现这个问题。如下&#xff1a; // const [xIs…

AngularJs指令中出错:Error: $compile:nonassign Non-Assignable Expression

Expression {resumeId: item.resumeId} used with directive rwdsDelete is non-assignable! 在AngularJS中&#xff0c;$compile服务用于将指令编译成HTML。当你在模板中使用了一个表达式&#xff0c;但这个表达式不是一个左值&#xff08;即不能被赋值的表达式&#xff09;时…

moviepy 将mp4视频文件提取音频mp3 - python 实现

DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” -------------------------------------------------------------…

openssl编译

关于windows下&#xff0c;openssl编译 环境准备 安装 perl:https://djvniu.jb51.net/200906/tools/ActivePerl5_64.rar安装nasm&#xff1a;https://www.nasm.us/pub/nasm/releasebuilds/2.13.01/win64/nasm-2.13.01-installer-x64.exe下载opensll源码&#xff1a;https://o…

QT c++ 样式 设置 标签(QLabel)的渐变色美化

上一篇文章中描述了按钮的纯色&#xff0c;本文描述标签的渐变色美化。 1.头文件 #ifndef WIDGET_H #define WIDGET_H #include <QWidget> //#include "CustomButton.h"#include <QVBoxLayout> #include <QLinearGradient> #include <QLabel…

学习打怪日记

目录 0 关于1 SpringBoot上传大文件抛出异常&#xff1a;MaxUploadSizeExceededException2 SpringBoot警告&#xff1a;HikariPool-1 - Failed to validate connection com.mysql.cj.jdbc.ConnectionImpl6221f160 (No operations allowed after connection closed.). Possibly …

xss-labs关卡记录15-20关

十五关 随便传一个参数&#xff0c;然后右击查看源码发现&#xff0c;这里有一个陌生的东西&#xff0c;就是ng-include。这里就是&#xff1a; ng-include指令就是文件包涵的意思&#xff0c;用来包涵外部的html文件&#xff0c;如果包涵的内容是地址&#xff0c;需要加引号。…

(五)ROS通信编程——参数服务器

前言 参数服务器在ROS中主要用于实现不同节点之间的数据共享&#xff08;P2P&#xff09;。参数服务器相当于是独立于所有节点的一个公共容器&#xff0c;可以将数据存储在该容器中&#xff0c;被不同的节点调用&#xff0c;当然不同的节点也可以往其中存储数据&#xff0c;关…

自动化测试框架搭建-数据库方法连接-初阶

目的 将数据库连接的步骤封装成一个方法&#xff0c;在需要连接数据库时&#xff0c;传入指定的参数&#xff08;SQL&#xff09;即可实现对数据查询和修改 代码实现 1、编写数据库连接方法 2、导入其他方法中使用步骤一 import pymysqldef mysqlConnetion(Sql):# 数据库连…

vivado 时钟指南

时钟指南 每个 FPGA 架构都为时钟提供有专用资源。掌握 FPGA 架构中的时钟资源&#xff0c;使您能够规划好自己的时钟&#xff0c;从而实现时钟 资源的最佳利用。大多数设计无需您了解这些细节。但如果您能够控制布局&#xff0c;同时对每个时钟域上的扇出有良好的思 路&a…