vue+node(socket.io)+webRTC实现一对一通话测试

VUE+WebRTC实现音视频直播 本文将介绍如何快速使用 Vue 实现一个简单的实时音视频通话。相关概念解释: - ZEGO Express SDK:由 ZEGO 提供的实时音视频 SDK,能够为开发者提供便捷接入、高清流畅、多平台互通、低延迟、高并发的音视频服务。- 推流:把采集阶段封包好的音视频数据流传输到 ZEGO 实时音视频云的过程。- 拉流:从 ZEGO 实时音视频云将已有音视频数据流进行拉取的过程。 阅读详情

<template>
  <div id="container">
   <h2>一对一视频通讯</h2>
   <div> 
       <button id="connServer" :disabled="connectFlag" @click="connServer">Connect Sig Server</button>
       <button id="leave" :disabled="leaveFlag" @click="leave">leave</button>

    </div>
    <div class="content">
      <div class="item">
        <h2>local:</h2>
        <video id="localVideo" playsinline autoplay muted width="340px" height="180px"></video>
        
      </div>
      <div class="item"> 
        <h2>remote:</h2>
        <video id="remoteVideo" playsinline autoplay width="340px" height="180px"></video>
       
      </div>
    </div>
  </div>
</template>
<script>
/**
 * 1、消息通讯的连接建立要在音视频数据获取之后,否则会导致有可能绑定音视频流失败
 * 2、当一端退出房间后另外一端的PeerConnection要关闭重建,否则与新用户互通时媒体协商会失败
 * 3、异步事件处理
 * 
 * **/
import { log } from "@/utils/logging";
export default {
  name: "VideoChat",
  components: {
  },
  data: () => ({
    // videos
    localVideo: {},
    remoteVideo: {},
    
    // Media config
    constraints: {
      audio: {
        noiseSuppression:true, 
        echoCancellation:true
      },
      video: {
        width:1920,
        height:1080,
        frameRate:30,
        facingMode:"environment"
      }
    },

    // local & remote video stream
    localStream: undefined,
    remoteStream: undefined,

    connectFlag:false,
    leaveFlag:false,

    pc:null,
    state:"init",

    roomid:'111111'
  }),
  sockets:{
      /**
       * socket自带3个事件connect,disconnect,reconnect
       * **/
      connect: function() {
        //与socket.io连接后回调
        console.log("socket connected");
      },
      disconnect:function () {
        console.log("socket disconnect");
      },
      reconnect:function () {
        console.log("socket reconnect");
      },
      joined:function (data) {
        console.log("joined",data)
        //加入成功后不能再次连接,可以离开
        this.connectFlag=true;
        this.leaveFlag=false;
        //改变状态
        this.state="joined";
        //创建了一个连接,并且将本地获取的音视频数据绑定到连接上,做好媒体协商的前提准备
        this.createPeerConnection();
        console.log("joined:state=",this.state)
      },
      otherjoin:function (data) {
        console.log("otherjoin",data)
        /**
         * 对应场景:a加入后b加入----a=>joined_conn,b=>joined
         *         a离开      ----a=>init,b=>joined_unbind
         *         c加入      ----b接受到otherjoin消息,此时b状态由joined_unbind=>joined_conn
         *                       由于之前的RTCPeerConnection已经关闭,需要重新创建RTCPeerConnection
         * **/
        if(this.state=="joined_unbind"){   
          this.createPeerConnection();
        }
        //改变状态
        this.state="joined_conn";//加入房间且可以音视频数据交互状态
        //媒体协商,主叫方开始创建offer
        this.call();

        console.log("otherjoin:state=",this.state)

      },
      full:function (data) {
        console.log("full:",data)
        this.state='leaved';
        //可以再次连接
        this.connectFlag=false;
        this.leaveFlag=true;
        console.log("full:state=",this.state);
        alert("rom is full");
        this.$socket.disconnect();
      },
      leaved:function (data) {
        console.log("leaved:",data)
        //已经离开了,可以再次连接
        this.connectFlag=false;
        this.leaveFlag=true;
        this.state='leaved';
        
        console.log("leaved:state=",this.state);
        //真的已经离开后才关闭连接
        this.$socket.disconnect();
      },
      bye:function (data) {
        console.log("bye:",data)
        this.state="joined_unbind";
        this.closePeerConnection();
        console.log("bye:state=",this.state);
      },
      message:function (msg) {
        console.log("received message:",msg)
        var data=msg[1];
        if(data){
          if(data.type=="offer"){
            console.log("received offer!")
            this.pc.setRemoteDescription(new RTCSessionDescription(data));
            this.pc.createAnswer().then((desc)=>{
              console.log("createAnswer success!");
              this.pc.setLocalDescription(desc).then(()=>{
                console.log("setLocalDescription success!")
              }).catch((err)=>{
                console.log("Failed setLocalDescription",err);
              });
              this.sendMessage(this.roomid,desc);
            }).catch((err)=>{
              console.log("Failed to getAnswer");
              console.log(err);
            });
          }else if(data.type=="answer"){
            console.log("received answer!")
            this.pc.setRemoteDescription(new RTCSessionDescription(data));
          }else if(data.type=="candidate"){

            /**
             * 设置setLocalDescription之后可以收集candidate,
             * 每当收集candidate的时候,触发pc的onicecandidate事件,
             * 此事件触发后会sendMessage,通知对方收集candidate
             * **/
            var candidate=new RTCIceCandidate({
              sdpMLineIndex:data.label,
              sdpMid:data.id,
              candidate:data.candidate
            })
            console.log("received candidate!",candidate)

            this.pc.addIceCandidate(candidate);
          }else{
            console.log("the message is  invalid!",data);
          }
        }
      }
    },
  created() {
  },
  mounted() {
    this.localVideo = document.getElementById("localVideo");
    this.remoteVideo = document.getElementById("remoteVideo");
  },
  methods: {
    //拿到媒体流
    async getUserMedia() {
      log(`Requesting  video stream`);

      if ("mediaDevices" in navigator) {
        try {
          const stream = await navigator.mediaDevices.getUserMedia(this.constraints);
          this.localVideo.srcObject = stream;
          this.localStream = stream;
          console.log("Received local video stream",this.localStream);
          //拿到本地媒体流后进行信令交互,创建peerConnection并进行媒体协商
          this.sendToService("join",this.roomid);
        } catch (error) {
          console.log(`getUserMedia error: ${error}`);
        }
      }
    },
    //创建offer发送给对方
    call(){
      if(this.state=="joined_conn"){  //只有呼叫方可以创建offer
        if(this.pc){
          var options={
            offerToReceiveAudio:1,
            offerToReceiveVideo:1
          }
          this.pc.createOffer(options).then((desc)=>{
            console.info("createOffer success!")
            this.pc.setLocalDescription(desc).then(()=>{
              console.log("setLocalDescription success!");
            }).catch((err)=>{
              console.log("setLocalDescription Failed!",err);
            });
            this.sendMessage(this.roomid,desc);
          }).catch((err)=>{
            console.log(err)
          })
        }
      }
    },
    sendMessage(roomid,data){
      if(this.$socket){
        this.$socket.emit("message",roomid,data);
      }
    },
    connServer(){
      if(this.$socket.disconnected){
        //console.log(this.$socket);
        this.$socket.connect();
      }
      this.getUserMedia();
      
    },
    sendToService(name,val) {
      this.$socket.emit(name,val);
    },
    leave(){
      if(this.$socket){
        this.sendToService("leave",this.roomid);
      }
      //关闭通道和本地媒体流
      this.closePeerConnection();
      this.closeLocalMedia();
    },
    createPeerConnection(){
      console.log("create RTCPeerConnection!");
      var _this=this;

      //创建webrtc连接对象
      if(!this.pc){
        // let pcConfig={
        //   'iceServers':[{
        //     'urls':'turn:stun.al.learningrtc.cn:3478',
        //     'credential':'mypasswd',
        //     'username':'garrylea'
        //   }]
        // }
      
        this.pc = new RTCPeerConnection();
        //做媒体协商时会触发此事件
        this.pc.onicecandidate=(e)=>{
          console.log("onicecandidate event is active",e)
          if(e.candidate){
            //接收到icecandidate后通知对方增加candidate(candidate的收集)
            console.log("find an new candidate",e.candidate);
            _this.sendMessage(_this.roomid,{
              type:'candidate',
              label:e.candidate.sdpMLineIndex,
              id:e.candidate.sdpMid,
              candidate:e.candidate.candidate
            })
          }
        }
        //打通通道,进行媒体协商结束后,接收到远方数据后触发,设置远程视频流地址
        this.pc.ontrack=(e)=>{
          console.log("ontrack event is active",e)

          if(e && e.streams[0]){
            _this.remoteVideo.srcObject = null;
            _this.remoteVideo.srcObject = e.streams[0];
          } 
        }
        //旧版api现在用ontrack代替
        // this.pc.onaddstream=(e)=>{
        //   console.log("onaddstream event is active",e)

        //   if(e && e.streams){
        //     _this.remoteVideo.srcObject = null;
        //     _this.remoteVideo.srcObject = e.streams;
        //   }
        // }
      }
      //将本地采集的数据添加到PeerConnection,这样在做媒体协商的时候就知道有哪些数据(必须先添加数据再做媒体协商)
      if(this.localStream){
        this.localStream.getTracks().forEach((track)=>{
          _this.pc.addTrack(track,this.localStream);
        })
        //旧版api现在用addTrack代替
        //_this.pc.addStream(this.localStream);
      }
    },
    closePeerConnection(){
      console.log("close RTCPeerConnection");
      if(this.pc){
        this.pc.close();
        this.pc=null;
      }
    },
    closeLocalMedia(){
      if(this.localStream && this.localStream.getTracks()){
        this.localStream.getTracks().forEach((track)=>{
          track.stop();
        })
      }
      this.localStream=null;
    }
    
  }
};
</script>

<style lang="less" scoped>
.content{
  width: 900px;
  height: 500px;
}
.item{
  float: left;
  height: 500px;
  width: 400px;
}
</style>

服务端代码

const express = require('express');
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server);

const USERCOUNT = 0;
// Chatroom

io.sockets.on("connection", (socket) => {
    // socket.emit() :向建立该连接的客户端广播
    // socket.broadcast.emit() :向除去建立该连接的客户端的所有客户端广播
    // io.sockets.emit() :向所有客户端广播,等同于上面两个的和
    console.log("服务端收到 connection");

    //接收到消息时触发
    socket.on('message', function(room, data) {
        console.log('服务端收到message : ', data.type);
        socket.to(room).emit('message', room, data);
    });


    //加入房间概念
    //加入房间
    socket.on('join', (room) => {
        socket.join(room);
        var myRoom = io.sockets.adapter.rooms[room];
        var users = (myRoom) ? Object.keys(myRoom.sockets).length : 0;
        console.log("join:当前房间有" + users + "人");
        //console.log(socket)
        if (users < USERCOUNT) {
            socket.emit('joined', room, socket.id); //给本人回
            if (users > 1) {
                socket.to(room).emit('otherjoin', room, socket.id); //给房间除自己以外的人
            }
        } else {
            socket.leave(room);
            socket.emit('full', room, socket.id); //给本人回
        }


    })

    //离开房间       
    socket.on('leave', (room) => {

        var myRoom = io.sockets.adapter.rooms[room];
        var users = (myRoom) ? Object.keys(myRoom.sockets).length : 0;
        //users-1
        socket.leave(room);
        console.log("leave:当前房间有" + Number(users - 1) + "人");

        socket.emit('leaved', room, socket.id); //给本人回
        socket.to(room).emit('bye', room, socket.id); //给房间除自己以外的人


    })

    // when the user disconnects.. perform this
    socket.on('disconnect', () => {
        console.log("服务端收到 disconnect");

    });
    socket.on('reconnect', () => {
        console.log("服务端收到 reconnect");

    });
    //发生错误时触发
    socket.on('error', function(err) {
        console.log(err);
    });
})

server.listen(3000, () => {
    console.log("监听3000端口");
});

 

webrtc实现视频通话(前端) 原生js+html实现webrtc视频通话功能 阅读详情

相关推荐

VUE2+WebRTC实现音视频直播

基于vue2搭建webRTC通信

Tuness的博客 2203

WebRTC本地实现 - WebRTC通信实现(Web端)

操作系统准备 需要准备好摄像头,由于本电脑没有摄像头,这里使用虚拟摄像头(VCam)来代替真实摄像头 效果展示 本地台式机使用VCam虚拟摄像头来模拟视频流 远程笔记本访问同一链接,从而进行通信 通信效果展示 代码提取 链接: https://pan.baidu.com/s/13BTXMQWJcPZqihgfN3jgZA 提取码: kyfu WebRTC通信实现明细 该文章代码是在 WebRTC本地实现 - Vue与Socket IO通信项目搭建与测试(https)基础上进行更改操作 .

翻肚鱼儿的博客 1911

WebRTC视频聊天和视频采集的例子

视频聊天 【从头到脚】撸一个多人视频聊天 — 前端 WebRTC 实战(一) WebRTC例子 vue+node(socket.io)+webRTC实现一对一通话测试 websocket实现实时直播(滑鸡版) 一对一视频聊天 以上都是demo, 不怎么适合于生产环境. 视频采集 Vue实现视频采集 这个很不错, 有点像拍短视频 H5实现摄像头的视频流数据的捕获 h5+实现手机端的录音,拍照,录像 视频采集 ...

wangbyby 393

Vue+WebRtc浏览器直接播放rtsp流

目录 前言 1、下载webrtc-streamer,本机测试我下载的最新window版本 2、解压下载包 3、双击webrtc-streamer.exe启动服务 4、将下载包html文件夹下webrtcstreamer.js文件和html/libs文件夹下adapter.min.js文件复制到VUE项目public目录下 5、编写测试页面 6、运行项目可查看监控视频播放效果 总结。能正常播放海康摄像机的rtsp数据流,但是换成华为摄像机的rtsp流,webrtc-streamer的服务就会自动断开。

zzp131797的博客 1183

安卓WebRTC实时音视频通信Demo开发指南

WebRTC作为开源实时通信技术,通过P2P连接实现低延迟的音视频传输。其核心技术包括STUN/TURN协议处理NAT穿透、ICE框架建立网络连接、以及媒体流传输控制。在安卓平台实现WebRTC需要处理硬件编解码适配、权限管理等特殊问题,本文通过Node.js+Vue+Android的完整技术栈,演示如何构建信令服务器、实现SDP交换与ICE协商,并解决华为设备视频旋转等常见兼容性问题。该方案适用于在线教育、视频会议等需要实时通信的场景,关键指标达到端到端延迟<300ms、分辨率640x480的实用水平。

weixin_34025151的博客 483

基于WebRTC与Socket.IO构建开源实时音视频聊天室:从原理到部署

实时音视频通信是现代Web应用的核心能力之一,其基础依赖于WebRTC(Web实时通信)技术。WebRTC通过在浏览器间建立点对点(P2P)连接,实现低延迟的音视频数据传输,其核心原理包括媒体捕获、信令交换、网络穿透(NAT/防火墙)和编解码。这项技术的价值在于为在线会议、远程协作、社交互动等场景提供了无需插件的原生通信能力。在实际工程中,信令服务器(如基于Socket.IO构建)负责协调会话的建立,而STUN/TURN服务器则解决复杂的网络穿透问题。本文以开源项目free4chat为例,深入解析了如何整合

weixin_28728279的博客 627

在express中集成vue_Vue + Node + WebRTC构建一个高逼格的视频应用

作者 | Adrián García Diéguez 译者 | 王强 编辑 | Yonie、张之栋 现在市面上有很多提供聊天和视频会议功能的免费应用,只需轻点几下鼠标,我们就能与世界各地的小伙伴愉快地交流。那么,你是否有兴趣构建一个属于自己的视频应用呢?让我们一起跟随作者,看看视频应用时如何构建的。 我们的应用程序主要有以下功能:一些聊天房间,可以来回切换...

weixin_39588209的博客 941

UniApp跨端WebRTC IPC客户端:从零构建原生性能的实时通信应用

本文详细介绍了如何使用UniApp和WebRTC构建跨端实时通信应用,重点解析了yangipcclient SDK的核心优势,包括原生视图渲染、C++统一处理和JavaScript胶水层。通过实战案例和优化技巧,帮助开发者快速实现低延迟、高性能的实时音视频通信,适用于视频客服、在线教育等场景。

weixin_29173777的博客 187

通信毕业设计选题偏软件?5个可落地的实战项目架构与实现指南

作为一名通信工程专业的过来人,我深知毕业设计选题的纠结。很多同学对硬件调试望而却步,想走软件方向,却又觉得通信协议深似海,不知从何下手,最终选题要么太“虚”(纯理论研究),要么太“重”(依赖昂贵硬件)。今天,我就结合自己的经验,分享几个偏软件、可落地、有深度的通信毕设项目思路,并手把手带你实现其中一个的核心部分。

2600_94960243的博客 991

前端即时通讯实战:从WebSocket原理到工程化架构设计

即时通讯(IM)作为现代Web应用的核心基础设施,其技术实现从基础的HTTP轮询、长轮询,演进到基于TCP的WebSocket全双工协议,实现了真正的低延迟双向通信。这项技术的核心价值在于解决了实时数据推送的工程挑战,广泛应用于在线协作、实时通知、数据看板等高交互场景。在复杂网络环境下,构建稳定可靠的IM系统需要深入理解连接管理、心跳保活、消息可靠投递等关键技术。本文通过对比WebSocket、SSE等主流方案,结合Service Worker、IndexedDB等现代浏览器能力,系统解析前端IM从协议选型

weixin_30621959的博客 487

Vue3 - 详解WebRTC实时通信技术做客户端1对1视频语音通话及挂断示例,全流程1v1双端音视频聊天源码,node服务端使用Socket.io搭建信令服务器处理通信双端进行媒体协商和ICE候选!

vue3,WebRTC,视频通话,一对一音视频聊天,webrtc在vue3中的使用,信令服务器,vue3移动端H5实现1v1视频聊天,基于WebRTC在Vue3中实现机内1v1机内视频通话,web实时语音视频通话,使用VUE3+WebRTC实现音视频通话教程,vue3如何搭建WebRTC双端聊天,node.js服务器,socket.io搭建信令服务器详细说明,WebRTC实现双端音视频聊天(Vue3+Node),搭建1对1视频聊天,多人语音聊天,连接不上怎么办,不显示画面,听不到声音,无音频,黑白屏没反应,

🏆 前端领域优质创作者 1万+

用快马AI 5分钟搭建WebRTC视频会议系统:零基础实战指南

搭建了一个WebRTC视频会议系统,整个过程比想象中简单太多。作为没接触过实时通信开发的小白,这里记录下从零开始的具体实现过程和踩坑经验。对于想快速验证WebRTC方案的开发者,这个平台确实大幅降低了试错成本。我的成品已经用在团队远程站会中,下一步计划加入录制功能和虚拟背景。输入"WebRTC视频会议"需求后,系统自动生成了包含信令交换、ICE协商等完整逻辑的初始代码,省去了查阅RFC文档的时间。内置的预览窗口可以直接测试设备兼容性,配合控制台日志快速定位媒体流问题。房间号采用时效性token。

SunstoneLion34的博客 400

Vue2 - 详解WebRTC实时通信技术做客户端1对1视频语音通话及挂断示例,node服务端使用Socket.io搭建信令服务器处理通信双端进行媒体协商和ICE候选(全流程1v1双端音视频聊天源码)

vue2,WebRTC,视频通话,一对一音视频聊天,webrtc在vue2中的使用,信令服务器,vue移动端H5实现1v1视频聊天,基于WebRTC在Vue中实现机内1v1机内视频通话,web实时语音视频通话,node.js服务器,socket.io搭建信令服务器详细说明,WebRTC实现双端音视频聊天(Vue2+Node),搭建1对1视频聊天,多人语音聊天,连接不上怎么办,不显示画面,听不到声音,无音频,黑白屏没反应,使用WebRTC技术实现一对一音视频通话,使用VUE+WebRTC实现音视频通话教程,v

🏆 前端领域优质创作者 6116

为失语者构建实时双向通话工具:技术选型、实现与部署全解析

语音合成(TTS)与实时音频流处理是构建现代通信应用的核心技术。其原理在于将文本信息通过深度学习模型转换为自然语音,并结合低延迟的流式传输协议,实现音频数据的实时双向交换。这项技术的价值在于能够桥接不同交互模式,为有特殊需求的用户(如语言障碍者)提供无障碍通信能力。典型的应用场景包括辅助通信工具、实时翻译服务和智能客服系统。本文聚焦于如何利用云通信API和流式TTS服务,构建一个服务于特定障碍人群的实时双向通话中继系统,其中涉及WebSocket通信、音频编解码和云服务集成等关键技术环节,并深入探讨了在真实

weixin_30325487的博客 357

从零搭建自动驾驶仿真环境:我的Nuplan-devkit实战笔记(含PyCharm配置技巧)

本文详细记录了从零搭建Nuplan-devkit自动驾驶仿真环境的完整流程,包括conda环境配置、pip版本控制、PyCharm专业版集成等关键步骤。特别针对Ubuntu系统下的依赖管理、数据目录优化和性能调优提供了实用技巧,帮助开发者高效构建稳定的自动驾驶仿真开发环境。

weixin_30505225的博客 479

Python + NX二次开发,如何配置Python的第三方库(包)?

地球人都知道python有强大的第三方库,可以各类骚操作,包括但不限于人工智能模块。在使用Python进行NX二次开发时,如果能够用得上这些第三方包,那将大大提高效率。本文详述了如何配置,实践证明:可行!

温良 1267

基于 Node.js 的 SC 语音自学交流平台

SC语音自学交流平台基于Node.js开发,整合语音学习与社交功能。系统采用Express/Koa框架构建后端,结合Socket.io实现实时语音交互,集成FFmpeg和语音识别API处理音频分析。平台提供语音素材库、发音评分、学习计划等自学功能,以及语音房间、作品社区等社交模块。技术优势包括低延迟通信、模块化扩展和跨平台兼容性,适用于语言学习者、播音爱好者等用户群体。通过"学-练-交流"闭环设计,帮助用户提升口语表达能力。(149字)

QQ1039692211的博客 649

群晖NAS安装HomeAssistant保姆级教程:从Docker部署到智能设备接入

本文提供了一份详尽的群晖NAS安装HomeAssistant保姆级教程,涵盖从Docker部署到智能设备接入的全过程。通过规划存储路径、配置容器环境变量和网络模式,确保系统稳定运行。教程还介绍了如何接入小米、苹果HomeKit等智能家居设备,并利用自动化与仪表盘打造本地化、高响应速度的智能中枢,实现数据隐私与远程控制的完美平衡。

play7的博客 693

CnOpenData 线上招聘网站信息数据

CnOpenData线上招聘网站信息数据库是一个覆盖中国主流招聘平台、时间跨度长、更新频率高的劳动力市场需求侧大数据集合。本数据库汇集了,为学术界和业界提供了一扇实时观察中国劳动力市场动态的窗口。

CnOpenData_wj的博客 2599

40岁从智驾转具身智能之路

我的工作经历比较复杂,本科毕业后工作2年再次读研的,本科和硕士都是机械专业,硕士毕业后干了5年CAM和机器人再转入到自动驾驶行业。在自动驾驶干了8年,发现自动驾驶已经到了一个瓶颈,再次转行做具身智能。我就谈谈自己的最近这次转型之路。

hzm8341的博客 637

浅谈车联网概述-1

车联网技术目前在车企中应用广泛,车联网通过车载传感器、无线通信等技术,能够将车辆上百的传感器数据上传至云端,当前智能网联车辆可以理解为一个“大号”手机。

Hello, World! 新能源汽车智能网联工程师,分享日常工作思考。 559

webRCT实时语音视频通话 结合 vue使用

vue中使用webRCT 语音视频通话

知所云 3733

用户行为数据分析 User Behavior Data from Taobao for Recommendation

一、提出问题 二、理解数据 数据来源 阿里云天池数据集:(https://tianchi.aliyun.com/dataset/dataDetail?dataId=649) 本文选取的数据集为UserBehavior.csv,共有5242880条数据。数据的具体类别如下表所示: 用户行为种类有四种,如下表所示 对于数据维度的说明如下: 在本文选取的2017年11月25日到2017年12月3日的时间范围内,不重复的用户数量为10202,不重复的商品ID数为412369,不重复的商品类目ID数为5859,

cccxxxqq的博客 5205

vue使用JSWebrtc播放webrtc视频流

vue使用JSWebrtc播放webrtc视频流

Gpointy的博客 2466

OpenCV图像去畸变实战指南:从原理到参数调优(附完整代码与效果对比)

本文详细解析了OpenCV图像去畸变的原理与实战方法,对比了三种核心函数(标准、鱼眼、全景模型)的适用场景与效果。重点提供了完整的代码示例,并深入探讨了alpha、焦距、中心点等关键参数的调优技巧,以帮助开发者有效矫正镜头畸变,提升计算机视觉任务精度。

life6的博客 553

uniapp:实现圆盘/色盘取色器组件,圆环颜色选择器用户可拖拽选择颜色值实时更新用户选择的颜色数据,拖动丝滑流畅不卡顿(uniapp小程序/手机h5网页/安卓苹果app/nvue都能使用,详细源码)

uniapp项目中使用颜色选择器,uniapp实现简单圆形色盘、取色器、拾色器组件,Uniapp自定义颜色选择器取色器,圆形的颜色选择器,uniapp颜色拾取器,拾色器 取色器 颜色选择器,uniapp vue3圆形的色盘取色,uni-app如何封装一个取色器组件,用uniapp写调色板,uniapp颜色选择器插件,实现圆环颜色选择器,Uni-app在App端使用颜色选择器,uniapp微信小程序圆盘颜色选择,uniapp H5网页实现圆形的取色器,使用canvas实现的简单圆形色盘,可自定义相关大小,un

✅ 精选推荐文章 4534
上一篇: vue+webRTC对webRTC基本api测试demo
下一篇: (chrome)vue+tracking实现人脸识别登录
github_39274378
博客等级 码龄9年 13粉丝 47原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值