mirror of
https://github.com/tl-open-source/tl-rtc-file.git
synced 2025-09-26 19:41:16 +08:00
feat: 支持单个rtc连接信息查看
feat: 支持远程连接语言,设备,网络,ice状态,ice类型展示 feat: 支持多端画布自适应比例 feat: 调整优化文本绘制 feat: 调整优化缓冲区满的日志 fix: 修复控制台版本打印 fix: 修复滚动条样式 fix: 修复页面多余节点 fix: 修复某些中英文引用 fix: 修复缓冲区满的阈值
This commit is contained in:
2
PAY.md
2
PAY.md
@@ -16,7 +16,7 @@
|
||||
- **价格:** 按功能大小,紧急程度,耗时,定制内容是否允许开源,等情况收费
|
||||
|
||||
## 收费模式
|
||||
- **按小时计费:** 根据实际工作时间计算费用,目前个人定制功能2小时起,每小时200~300,企业定制另谈
|
||||
- **按小时计费:** 根据实际工作时间计算费用,目前个人定制功能2小时起,每小时200~300
|
||||
- **阶段性付费:** 确认开发前,预付1/3,开发完成付1/3,交付上线1/3
|
||||
|
||||
## 付款方式
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "10.2.5",
|
||||
"version": "10.2.6",
|
||||
"ws": {
|
||||
"port": 8444,
|
||||
"host": "ws://127.0.0.1:8444"
|
||||
|
@@ -284,7 +284,7 @@ body {
|
||||
}
|
||||
|
||||
.tl-rtc-file-user-body {
|
||||
display: flex;
|
||||
display: -webkit-box;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@@ -1134,3 +1134,28 @@ body {
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
|
||||
.remote_user_info{
|
||||
text-align: left;
|
||||
padding: 20px 20px 0 20px;
|
||||
}
|
||||
|
||||
.remote_user_info div{
|
||||
padding-bottom: 5px;
|
||||
font-weight: 200;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.remote_user_info div b{
|
||||
color: #548726;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.layui-carousel[lay-indicator=outside] .layui-carousel-ind{
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.layui-carousel>[carousel-item]{
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
@@ -7,8 +7,8 @@
|
||||
<meta name="renderer" content="webkit">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0">
|
||||
<script src="static/layui/layui.js" v="layui" e="layui"></script>
|
||||
<link rel="stylesheet" type="text/css" href="static/layui/css/layui.css" />
|
||||
<script src="/static/layui/layui.js" v="layui" e="layui"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/layui/css/layui.css" />
|
||||
<link href="/image/44826979.png" rel="shortcut icon" type="image/x-icon">
|
||||
</head>
|
||||
|
||||
|
@@ -77,7 +77,7 @@
|
||||
<use xlink:href="#icon-rtc-file-five-g"></use>
|
||||
</svg>
|
||||
<svg class="icon" aria-hidden="true" v-show="network === 'wifi'"
|
||||
style="width: 20px;height: 20px;margin-right: 10px;">
|
||||
style="scale: 1.3;width: 20px;height: 20px;margin-right: 10px;">
|
||||
<use xlink:href="#icon-rtc-file-WIFI"></use>
|
||||
</svg>
|
||||
</a>
|
||||
@@ -90,14 +90,14 @@
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="screenShareTimes" v-show="isScreenShare">
|
||||
{{lang.sharing}}: {{screenShareTimes < 10 ? '0' + screenShareTimes :
|
||||
screenShareTimes}}{{lang.second}} </b>
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="videoShareTimes" v-show="isVideoShare">
|
||||
{{lang.videoing}}: {{videoShareTimes < 10 ? '0' + videoShareTimes :
|
||||
videoShareTimes}}{{lang.second}} </b>
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="liveShareTimes"
|
||||
v-show="isLiveShare">
|
||||
{{lang.living}}: {{liveShareTimes < 10 ? '0' + liveShareTimes :
|
||||
liveShareTimes}}{{lang.second}} </b>
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="screenTimes"></b>
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="videoShareTimes" v-show="isVideoShare">
|
||||
{{lang.videoing}}: {{videoShareTimes < 10 ? '0' + videoShareTimes :
|
||||
videoShareTimes}}{{lang.second}} </b>
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="liveShareTimes"
|
||||
v-show="isLiveShare && owner">
|
||||
{{lang.living}}: {{liveShareTimes < 10 ? '0' + liveShareTimes :
|
||||
liveShareTimes}}{{lang.second}} </b>
|
||||
<b style="transition: color 0.8s;margin-right: 5px;" id="screenTimes"></b>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -258,7 +258,6 @@
|
||||
<b>{{lang.pickup_code}}</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layui-col-xs3 swiper-slide" @click="openRemoteDraw"
|
||||
:class="switchData.openRemoteDraw ? '':'tl-rtc-file-tool-disabled'">
|
||||
<div class="tl-rtc-file-tool tl-rtc-file-tool-mobile" v-if="clientWidth < 450">
|
||||
@@ -316,10 +315,12 @@
|
||||
</svg>
|
||||
<div class="tl-rtc-file-user-body-left">
|
||||
<b class="tl-rtc-file-user-body-left-nick">
|
||||
<b v-show="owner" style="color: #7375e9;">【{{lang.owner}}】- </b>
|
||||
{{nickName}} - {{lang.self}}
|
||||
<b v-show="owner" style="color: #7375e9;">【{{lang.owner}}】-</b>
|
||||
【{{lang.self}}】- {{nickName}}
|
||||
</b>
|
||||
<b class="tl-rtc-file-user-body-left-id">
|
||||
{{socketId}}
|
||||
</b>
|
||||
<b class="tl-rtc-file-user-body-left-id"> {{socketId}} </b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tl-rtc-file-user-body-right">
|
||||
@@ -346,7 +347,7 @@
|
||||
|
||||
<!-- 远端连接 -->
|
||||
<div class="layui-row" style="position: relative;">
|
||||
<div class="layui-col-xs12 tl-rtc-file-user-list" v-for="remote in remoteMap">
|
||||
<div class="layui-col-xs12 tl-rtc-file-user-list" v-for="remote in remoteMap" @click="showRemoteUser(remote)">
|
||||
<div class="tl-rtc-file-user">
|
||||
<div class="tl-rtc-file-user-body">
|
||||
<svg class="icon" aria-hidden="true" style="width: 32px;height: 32px;">
|
||||
@@ -355,13 +356,22 @@
|
||||
<div class="tl-rtc-file-user-body-left">
|
||||
<b class="tl-rtc-file-user-body-left-nick">
|
||||
<b v-show="remote.owner" style="color: #7375e9;">【{{lang.owner}}】- </b>
|
||||
{{remote.nickName}}
|
||||
{{remote.nickName}} - {{remote.id}}
|
||||
</b>
|
||||
<b class="tl-rtc-file-user-body-left-id">
|
||||
<svg class="icon" aria-hidden="true" style="width: 12px;height: 12px;">
|
||||
<use xlink:href="#icon-rtc-file-kongzhuangzhongduan"></use>
|
||||
</svg> : {{remote.langMode}} - {{remote.ua}} - {{remote.network}} -
|
||||
<svg class="icon" aria-hidden="true" style="width: 12px;height: 12px;">
|
||||
<use xlink:href="#icon-rtc-file-yunfuwuqi"></use>
|
||||
</svg> :
|
||||
<b v-if="iceOk(remote.iceConnectionState)" style="font-weight: bold;color: #69d31e;">{{remote.iceConnectionState}} - {{remote.p2pMode}}</b>
|
||||
<b v-else style="font-weight: bold;color: red;">{{remote.iceConnectionState}} - {{remote.p2pMode}}</b>
|
||||
</b>
|
||||
<b class="tl-rtc-file-user-body-left-id">{{remote.langMode}} - {{remote.id}} </b>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tl-rtc-file-user-body-right">
|
||||
<i :title="lang.send_text" @click="startChatRoomSingle(remote)"
|
||||
<i :title="lang.send_text" @click="startChatRoomSingle(event, remote)"
|
||||
class="layui-icon layui-icon-reply-fill"></i>
|
||||
<span v-show="remote.receiveChatRoomSingleList.length > 0" style="right: -7px;"
|
||||
class="layui-badge tl-rtc-file-msg-dot layui-anim layui-anim-rotate layui-anim-loop">
|
||||
@@ -424,7 +434,7 @@
|
||||
</div>
|
||||
|
||||
<div class="layui-row tl-rtc-file-send-list-all"
|
||||
:style="{height: sendFileRecoderHeight+'px',overflowY: (sendFileRecoderList.length > 1 ? 'scroll' : 'none') }">
|
||||
:style="{height: sendFileRecoderHeight+'px',overflowY: (sendFileRecoderList.length > 1 ? 'auto' : 'none') }">
|
||||
<div class="layui-col-xs12 tl-rtc-file-send-list send-file-item"
|
||||
v-for="file, index in sendFileRecoderList" :id="'send-file-item'+index">
|
||||
<div class="tl-rtc-file-send" :style="{'--progress': file.progress + '%'}">
|
||||
@@ -615,7 +625,7 @@
|
||||
style="cursor: pointer; right: 10px;position: absolute;"></i>
|
||||
</div>
|
||||
<div class="layui-row tl-rtc-file-send-list-all"
|
||||
:style="{height: chooseFileHeight+'px',overflowY: (chooseFileList.length > 1 ? 'scroll' : 'none') }">
|
||||
:style="{height: chooseFileHeight+'px',overflowY: (chooseFileList.length > 1 ? 'auto' : 'none') }">
|
||||
<div class="layui-col-xs12 tl-rtc-file-send-list" v-for="file, index in chooseFileList">
|
||||
<div class="tl-rtc-file-send" :style="{'--progress': file.progress + '%'}">
|
||||
<div class="tl-rtc-file-send-body">
|
||||
@@ -691,7 +701,7 @@
|
||||
style="cursor: pointer; right: 10px;position: absolute;"></i>
|
||||
</div>
|
||||
<div class="layui-row tl-rtc-file-send-list-all"
|
||||
:style="{height: sendFileRecoderHistoryHeight+'px',overflowY: (sendFileRecoderHistoryList.length > 1 ? 'scroll' : 'none') }">
|
||||
:style="{height: sendFileRecoderHistoryHeight+'px',overflowY: (sendFileRecoderHistoryList.length > 1 ? 'auto' : 'none') }">
|
||||
<div class="layui-col-xs12 tl-rtc-file-send-list" v-for="file, index in sendFileRecoderHistoryList">
|
||||
<div class="tl-rtc-file-send" :style="{'--progress': file.progress + '%'}">
|
||||
<div class="tl-rtc-file-send-body">
|
||||
@@ -771,7 +781,7 @@
|
||||
style="cursor: pointer; right: 10px;position: absolute;"></i>
|
||||
</div>
|
||||
<div class="layui-row tl-rtc-file-send-list-all"
|
||||
:style="{height: receiveFileHeight+'px',overflowY: (receiveFileRecoderList.length > 1 ? 'scroll' : 'none')}">
|
||||
:style="{height: receiveFileHeight+'px',overflowY: (receiveFileRecoderList.length > 1 ? 'auto' : 'none')}">
|
||||
<div class="layui-col-xs12 tl-rtc-file-send-list" v-for="file in receiveFileRecoderList">
|
||||
<div class="tl-rtc-file-send" :style="{'--progress': file.progress + '%'}">
|
||||
<div class="tl-rtc-file-send-body">
|
||||
@@ -867,7 +877,7 @@
|
||||
</div>
|
||||
|
||||
<div class="layui-row tl-rtc-file-send-list-all"
|
||||
:style="{height: codeFileHeight+'px',overflowY: (receiveCodeFileList.length > 1 ? 'scroll' : 'none') }">
|
||||
:style="{height: codeFileHeight+'px',overflowY: (receiveCodeFileList.length > 1 ? 'auto' : 'none') }">
|
||||
<div class="layui-col-xs12 tl-rtc-file-send-list" v-for="file, index in receiveCodeFileList">
|
||||
<div class="tl-rtc-file-send">
|
||||
<div class="tl-rtc-file-send-body">
|
||||
@@ -955,7 +965,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'scroll'}">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body tl-rtc-file-mask-log" v-for="log in filterLogs">
|
||||
<div class="tl-rtc-file-mask-log-time">
|
||||
<div>{{log.time}}</div>
|
||||
@@ -964,7 +974,7 @@
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="log.msg.length >= 500" class="tl-rtc-file-mask-log-msg"
|
||||
style="max-height: 300px; overflow-y: scroll;">
|
||||
style="max-height: 300px; overflow-y: auto;">
|
||||
<b v-show="log.type === '【{{lang.sys_log}}】: '" style="color: #b54343;">{{log.type}}</b>
|
||||
<b v-show="log.type === '【{{lang.op_log}}】: '"
|
||||
style="color: cadetblue;">{{log.type}}</b>
|
||||
@@ -993,7 +1003,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'scroll'}">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body">
|
||||
<div class="tl-rtc-file-mask-media-container " id="mediaVideoRoomList"> </div>
|
||||
</div>
|
||||
@@ -1013,7 +1023,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'scroll'}">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body">
|
||||
<div class="tl-rtc-file-mask-media-container " id="mediaScreenRoomList"> </div>
|
||||
</div>
|
||||
@@ -1033,7 +1043,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="layui-card">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'scroll'}">
|
||||
<div :style="{height: logsHeight+'px',overflowY: 'auto'}">
|
||||
<div class="layui-card-body">
|
||||
<div class="tl-rtc-file-mask-media-container " id="mediaLiveRoomList"> </div>
|
||||
</div>
|
||||
@@ -1057,10 +1067,11 @@
|
||||
layui.use([
|
||||
'layedit', 'form', 'layer', 'laytpl', 'upload',
|
||||
'dropdown', 'carousel', 'util', 'colorpicker',
|
||||
'slider', 'dropdown'
|
||||
'slider', 'dropdown', 'carousel'
|
||||
], function () {
|
||||
window.layer = layui.layer;
|
||||
window.form = layui.form;
|
||||
window.carousel = layui.carousel;
|
||||
window.$ = layui.$;
|
||||
window.layedit = layui.layedit;
|
||||
window.laytpl = layui.laytpl;
|
||||
|
@@ -286,38 +286,8 @@ window.tlrtcfile = {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
getWebrtcStats: async function (peerConnection) {
|
||||
// 候选者对
|
||||
"candidate-pair" |
|
||||
// 证书相关的统计信息
|
||||
"certificate" |
|
||||
// 当前音视频编解码器的统计信息
|
||||
"codec" |
|
||||
// CSRC相关的统计信息
|
||||
"csrc" |
|
||||
// 数据通道的相关统计信息
|
||||
"data-channel" |
|
||||
// 传入数据流的相关统计信息
|
||||
"inbound-rtp" |
|
||||
// 本地候选连接的相关统计信息
|
||||
"local-candidate" |
|
||||
// 媒体源的相关统计信息
|
||||
"media-source" |
|
||||
// 传出数据流的相关统计信息
|
||||
"outbound-rtp" |
|
||||
// 对等连接的相关统计信息
|
||||
"peer-connection" |
|
||||
// 对等连接的相关统计信息
|
||||
"remote-candidate" |
|
||||
// 远程传入数据流的相关统计信息
|
||||
"remote-inbound-rtp" |
|
||||
// 远程传出数据流的相关统计信息
|
||||
"remote-outbound-rtp" |
|
||||
// 媒体轨道的相关统计信息
|
||||
"track" |
|
||||
// 传输协议的相关统计信息
|
||||
"transport";
|
||||
|
||||
if (!peerConnection) {
|
||||
return "RTCPeerConnection is not available";
|
||||
}
|
||||
@@ -325,23 +295,63 @@ window.tlrtcfile = {
|
||||
return "RTCStatsReport is not available";
|
||||
}
|
||||
|
||||
let result = { }
|
||||
|
||||
let stats = await peerConnection.getStats(null);
|
||||
|
||||
stats.forEach((report) => {
|
||||
if (!report.type) return;
|
||||
let data = {}
|
||||
Object.keys(report).forEach((statName) => {
|
||||
data[statName] = report[statName]
|
||||
});
|
||||
result[report.type] = {
|
||||
kind : report.kind,
|
||||
data : data
|
||||
function getTypeDescription(type) {
|
||||
switch (type) {
|
||||
case 'candidate-pair':
|
||||
return '候选者对';
|
||||
case 'certificate':
|
||||
return '证书相关的统计信息';
|
||||
case 'codec':
|
||||
return '当前音视频编解码器的统计信息';
|
||||
case 'csrc':
|
||||
return 'CSRC相关的统计信息';
|
||||
case 'data-channel':
|
||||
return '数据通道的相关统计信息';
|
||||
case 'inbound-rtp':
|
||||
return '传入数据流的相关统计信息';
|
||||
case 'local-candidate':
|
||||
return '本地候选连接的相关统计信息';
|
||||
case 'media-source':
|
||||
return '媒体源的相关统计信息';
|
||||
case 'outbound-rtp':
|
||||
return '传出数据流的相关统计信息';
|
||||
case 'peer-connection':
|
||||
return '对等连接的相关统计信息';
|
||||
case 'remote-candidate':
|
||||
return '远程候选连接的相关统计信息';
|
||||
case 'remote-inbound-rtp':
|
||||
return '远程传入数据流的相关统计信息';
|
||||
case 'remote-outbound-rtp':
|
||||
return '远程传出数据流的相关统计信息';
|
||||
case 'track':
|
||||
return '媒体轨道的相关统计信息';
|
||||
case 'transport':
|
||||
return '传输协议的相关统计信息';
|
||||
case 'media-playout':
|
||||
return '音频播放的相关统计数据'
|
||||
default:
|
||||
return '未知类型';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result
|
||||
function getRTCStats(peerConnection) {
|
||||
const statsMap = new Map();
|
||||
return new Promise((resolve) => {
|
||||
peerConnection.getStats().then((stats) => {
|
||||
stats.forEach((report) => {
|
||||
const { type } = report;
|
||||
if (!statsMap.has(type)) {
|
||||
statsMap.set(type, []);
|
||||
}
|
||||
statsMap.get(type).push({ report, description: getTypeDescription(type) });
|
||||
});
|
||||
|
||||
resolve(statsMap);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return await getRTCStats(peerConnection);
|
||||
},
|
||||
copyTxt: function (id, content) {
|
||||
let that = this;
|
||||
|
@@ -17,6 +17,8 @@ const draw = new Vue({
|
||||
drawHistoryList: [], // 绘制历史操作列表, 用于回退
|
||||
drawRollbackPoint: 0, // 绘制回退点
|
||||
lineWidth: 1, // 画笔线宽
|
||||
lineCap : "round",
|
||||
lineJoin : "round",
|
||||
strokeStyle: "#000000", // 画笔颜色
|
||||
//line: 线条, circle: 圆形, rectangle: 矩形, text: 文字, delete: 擦除
|
||||
drawMode: "line", // 画笔模式
|
||||
@@ -26,7 +28,7 @@ const draw = new Vue({
|
||||
starFill : false, //填充星星
|
||||
rhomboidFill : false, //填充平行四边形
|
||||
hexagonFill : false, //填充六边形
|
||||
circleStarPoint: { x: 0, y: 0 }, //圆形开始点
|
||||
circleStartPoint: { x: 0, y: 0 }, //圆形开始点
|
||||
triangleStartPoint : { x: 0, y: 0 }, //三角形开始点
|
||||
starStartPoint : { x: 0, y: 0 }, //星星开始点
|
||||
rhomboidStartPoint : { x: 0, y: 0 }, //平行四边形开始点
|
||||
@@ -106,39 +108,66 @@ const draw = new Vue({
|
||||
if (!this.isOpenDraw) {
|
||||
return
|
||||
}
|
||||
const { drawMode, event } = options;
|
||||
const canvas = document.getElementById('tl-rtc-file-mouse-draw-canvas');
|
||||
options.canvas = canvas;
|
||||
options.context = canvas.getContext('2d');
|
||||
options.fromRemote = true;
|
||||
|
||||
const { drawMode } = options;
|
||||
const context = canvas.getContext('2d');
|
||||
options.remote.canvas = canvas;
|
||||
options.remote.context = context;
|
||||
|
||||
//收到结束标识,保存当前画板到缓存数据中
|
||||
if(options.event === 'end'){
|
||||
this.endDrawHandler(options)
|
||||
if(event === 'end'){
|
||||
this.endDrawHandler({canvas, context})
|
||||
return
|
||||
}
|
||||
|
||||
let {
|
||||
width : remoteWidth, height : remoteHeight, lineWidth,
|
||||
curPoint, prePoint, starStartPoint, circleStartPoint, triangleStartPoint, rectangleStartPoint
|
||||
} = options.remote;
|
||||
|
||||
//计算双方画布比例,按比例进行坐标放大/缩小
|
||||
const ratioWidth = canvas.width / remoteWidth;
|
||||
const ratioHeight = canvas.height / remoteHeight;
|
||||
|
||||
//调整画笔
|
||||
options.remote.lineWidth = lineWidth * (ratioWidth + ratioHeight) / 2
|
||||
curPoint.x = curPoint.x * ratioWidth;
|
||||
curPoint.y = curPoint.y * ratioHeight;
|
||||
options.remote.curPoint = curPoint;
|
||||
|
||||
if (drawMode === 'line') {
|
||||
prePoint.x = prePoint.x * ratioWidth;
|
||||
prePoint.y = prePoint.y * ratioHeight;
|
||||
options.remote.prePoint = prePoint;
|
||||
this.drawLine(options);
|
||||
} else if (drawMode === 'circle') {
|
||||
circleStartPoint.x = circleStartPoint.x * ratioWidth;
|
||||
circleStartPoint.y = circleStartPoint.y * ratioHeight;
|
||||
options.remote.circleStartPoint = circleStartPoint;
|
||||
this.drawCircle(options);
|
||||
} else if (drawMode === 'rectangle') {
|
||||
rectangleStartPoint.x = rectangleStartPoint.x * ratioWidth;
|
||||
rectangleStartPoint.y = rectangleStartPoint.y * ratioHeight;
|
||||
options.remote.rectangleStartPoint = rectangleStartPoint;
|
||||
this.drawRectangle(options);
|
||||
} else if (drawMode === 'text') {
|
||||
this.drawText(options);
|
||||
} else if(drawMode === 'triangle'){
|
||||
triangleStartPoint.x = triangleStartPoint.x * ratioWidth;
|
||||
triangleStartPoint.y = triangleStartPoint.y * ratioHeight;
|
||||
options.remote.triangleStartPoint = triangleStartPoint;
|
||||
this.drawTriangle(options);
|
||||
} else if(drawMode === 'star'){
|
||||
starStartPoint.x = starStartPoint.x * ratioWidth;
|
||||
starStartPoint.y = starStartPoint.y * ratioHeight;
|
||||
options.remote.starStartPoint = starStartPoint;
|
||||
this.drawStar(options);
|
||||
} else {
|
||||
console.log("收到远程未知的绘制模式")
|
||||
}
|
||||
},
|
||||
// 打开/关闭本地画笔
|
||||
openDraw: function ({
|
||||
openCallback, closeCallback, localDrawCallback
|
||||
}) {
|
||||
openDraw: function ({ openCallback, closeCallback, localDrawCallback }) {
|
||||
let that = this;
|
||||
|
||||
if (this.isOpenDraw) {
|
||||
@@ -466,126 +495,185 @@ const draw = new Vue({
|
||||
that.drawing = false;
|
||||
};
|
||||
},
|
||||
// 开始绘制
|
||||
startDrawHandler: function ({ canvas, context, localDrawCallback }) {
|
||||
//公共参数
|
||||
let commOptions = {
|
||||
event : "start",
|
||||
let localCommOptions = {
|
||||
canvas,
|
||||
context,
|
||||
localDrawCallback,
|
||||
drawMode: this.drawMode,
|
||||
devicePixelRatio : this.devicePixelRatio,
|
||||
width : canvas.width,
|
||||
height: canvas.height,
|
||||
lineCap: this.lineCap,
|
||||
lineJoin: this.lineJoin,
|
||||
lineWidth: this.lineWidth,
|
||||
strokeStyle: this.strokeStyle,
|
||||
fillStyle: this.strokeStyle,
|
||||
lineCap: "round",
|
||||
lineJoin: "round"
|
||||
}
|
||||
|
||||
|
||||
if (this.drawMode === 'delete') {
|
||||
this.drawDelete(Object.assign(commOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint : this.prePoint
|
||||
}))
|
||||
this.drawDelete({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint : this.prePoint
|
||||
}),
|
||||
})
|
||||
} else if (this.drawMode === 'rectangle') {
|
||||
//开始的时候固定好矩形的起点
|
||||
this.rectangleStartPoint = this.prePoint;
|
||||
this.drawRectangle(Object.assign(commOptions, {
|
||||
rectangleStartPoint: this.rectangleStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
rectangleFill : this.rectangleFill
|
||||
}))
|
||||
this.drawRectangle({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
rectangleStartPoint: this.rectangleStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
rectangleFill : this.rectangleFill
|
||||
}),
|
||||
})
|
||||
} else if (this.drawMode === 'circle') {
|
||||
//开始的时候固定好圆的起点
|
||||
this.circleStartPoint = this.prePoint;
|
||||
this.drawCircle(Object.assign(commOptions, {
|
||||
circleStartPoint: this.circleStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
circleFill : this.circleFill,
|
||||
}))
|
||||
this.drawCircle({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
circleStartPoint: this.circleStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
circleFill : this.circleFill,
|
||||
}),
|
||||
})
|
||||
} else if(this.drawMode === 'triangle'){
|
||||
//开始的时候固定好三角形的起点
|
||||
this.triangleStartPoint = this.prePoint;
|
||||
this.drawTriangle(Object.assign(commOptions, {
|
||||
triangleStartPoint: this.triangleStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
triangleFill : this.triangleFill
|
||||
}));
|
||||
this.drawTriangle({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
triangleStartPoint: this.triangleStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
triangleFill : this.triangleFill
|
||||
}),
|
||||
})
|
||||
} else if(this.drawMode === 'star'){
|
||||
//开始的时候固定好星星的起点
|
||||
this.starStartPoint = this.prePoint;
|
||||
this.drawStar(Object.assign(commOptions, {
|
||||
starStartPoint: this.starStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
starFill : this.starFill
|
||||
}));
|
||||
this.drawStar({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
starStartPoint: this.starStartPoint,
|
||||
curPoint: this.prePoint,
|
||||
starFill : this.starFill
|
||||
}),
|
||||
})
|
||||
} else if (this.drawMode === 'text') {
|
||||
this.drawText(Object.assign(commOptions, {
|
||||
curPoint: this.prePoint,
|
||||
}))
|
||||
this.endDrawHandler(Object.assign(commOptions, {
|
||||
this.drawText({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
curPoint: this.prePoint,
|
||||
}),
|
||||
})
|
||||
this.endDrawHandler(Object.assign(localCommOptions, {
|
||||
curPoint: this.prePoint,
|
||||
}))
|
||||
} else if(this.drawMode === 'line'){
|
||||
this.drawLine(Object.assign(commOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint: this.prePoint,
|
||||
}));
|
||||
this.drawLine({
|
||||
event : "start",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint: this.prePoint,
|
||||
}),
|
||||
})
|
||||
}
|
||||
},
|
||||
// 绘制中
|
||||
drawingHandler: function ({ canvas, curPoint, context, localDrawCallback }) {
|
||||
if (!this.drawing) {
|
||||
return
|
||||
}
|
||||
|
||||
//公共参数
|
||||
let commOptions = {
|
||||
event : "move",
|
||||
let localCommOptions = {
|
||||
canvas,
|
||||
context,
|
||||
localDrawCallback,
|
||||
drawMode: this.drawMode,
|
||||
devicePixelRatio : this.devicePixelRatio,
|
||||
width : canvas.width,
|
||||
height: canvas.height,
|
||||
lineCap: this.lineCap,
|
||||
lineJoin: this.lineJoin,
|
||||
lineWidth: this.lineWidth,
|
||||
strokeStyle: this.strokeStyle,
|
||||
fillStyle: this.strokeStyle,
|
||||
lineCap: "round",
|
||||
lineJoin: "round"
|
||||
}
|
||||
|
||||
if (this.drawMode === 'delete') {
|
||||
this.drawDelete(Object.assign(commOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint
|
||||
}))
|
||||
this.drawDelete({
|
||||
event : "move",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint
|
||||
}),
|
||||
})
|
||||
} else if (this.drawMode === 'rectangle') {
|
||||
this.drawRectangle(Object.assign(commOptions, {
|
||||
rectangleStartPoint: this.rectangleStartPoint,
|
||||
curPoint,
|
||||
rectangleFill : this.rectangleFill
|
||||
}));
|
||||
this.drawRectangle({
|
||||
event : "move",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
rectangleStartPoint: this.rectangleStartPoint,
|
||||
curPoint,
|
||||
rectangleFill : this.rectangleFill
|
||||
}),
|
||||
})
|
||||
} else if (this.drawMode === 'circle') {
|
||||
this.drawCircle(Object.assign(commOptions, {
|
||||
circleStartPoint: this.circleStartPoint,
|
||||
curPoint,
|
||||
circleFill : this.circleFill,
|
||||
}))
|
||||
this.drawCircle({
|
||||
event : "move",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
circleStartPoint: this.circleStartPoint,
|
||||
curPoint,
|
||||
circleFill : this.circleFill,
|
||||
}),
|
||||
})
|
||||
} else if(this.drawMode === 'triangle'){
|
||||
this.drawTriangle(Object.assign(commOptions, {
|
||||
triangleStartPoint: this.triangleStartPoint,
|
||||
curPoint,
|
||||
triangleFill : this.triangleFill
|
||||
}));
|
||||
this.drawTriangle({
|
||||
event : "move",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
triangleStartPoint: this.triangleStartPoint,
|
||||
curPoint,
|
||||
triangleFill : this.triangleFill
|
||||
}),
|
||||
|
||||
})
|
||||
} else if(this.drawMode === 'star'){
|
||||
this.drawStar(Object.assign(commOptions, {
|
||||
starStartPoint: this.starStartPoint,
|
||||
curPoint,
|
||||
starFill : this.starFill
|
||||
}));
|
||||
this.drawStar({
|
||||
event : "move",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
starStartPoint: this.starStartPoint,
|
||||
curPoint,
|
||||
starFill : this.starFill
|
||||
}),
|
||||
})
|
||||
} else if(this.drawMode === 'line'){
|
||||
this.drawLine(Object.assign(commOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint,
|
||||
}));
|
||||
this.drawLine({
|
||||
event : "move",
|
||||
drawMode: this.drawMode,
|
||||
local : Object.assign(localCommOptions, {
|
||||
prePoint: this.prePoint,
|
||||
curPoint,
|
||||
}),
|
||||
})
|
||||
}
|
||||
},
|
||||
// 结束绘制
|
||||
endDrawHandler: function ({ canvas, curPoint, context, localDrawCallback }) {
|
||||
//图像记录,用于回滚撤销操作
|
||||
if (
|
||||
@@ -600,13 +688,12 @@ const draw = new Vue({
|
||||
//结束的时候通知下远程,可以保存画布到缓存列表中
|
||||
localDrawCallback && localDrawCallback({
|
||||
event : "end",
|
||||
drawMode : this.drawMode
|
||||
drawMode : this.drawMode,
|
||||
remote : {}
|
||||
})
|
||||
},
|
||||
//下载画布图片
|
||||
drawDownload: function ( options ) {
|
||||
const { canvas, context, localDrawCallback } = options;
|
||||
|
||||
// 下载画布图片
|
||||
drawDownload: function ({ canvas, context, localDrawCallback }) {
|
||||
let image = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
|
||||
let link = document.createElement('a');
|
||||
link.href = image;
|
||||
@@ -614,16 +701,13 @@ const draw = new Vue({
|
||||
link.click();
|
||||
},
|
||||
// 画布重置
|
||||
drawReset: function (options) {
|
||||
const { canvas, context, localDrawCallback } = options;
|
||||
|
||||
drawReset: function ({ canvas, context, localDrawCallback }) {
|
||||
context.clearRect(0, 0, canvas.width, canvas.height);
|
||||
this.drawHistoryList = [];
|
||||
this.drawRollbackPoint = 0;
|
||||
},
|
||||
// 回退回滚的绘制
|
||||
drawUndoRollback: function (options) {
|
||||
const { canvas, context, localDrawCallback } = options;
|
||||
drawUndoRollback: function ({ canvas, context, localDrawCallback }) {
|
||||
//最多前进到最后一条记录
|
||||
if (this.drawRollbackPoint < this.drawHistoryList.length - 1) {
|
||||
this.drawRollbackPoint = this.drawRollbackPoint + 1;
|
||||
@@ -637,8 +721,7 @@ const draw = new Vue({
|
||||
}
|
||||
},
|
||||
// 画布回退
|
||||
drawRollback: async function (options) {
|
||||
const { canvas, context, localDrawCallback } = options;
|
||||
drawRollback: async function ({ canvas, context, localDrawCallback }) {
|
||||
//最多回退到原点
|
||||
if (this.drawRollbackPoint > 0) {
|
||||
this.drawRollbackPoint = this.drawRollbackPoint - 1;
|
||||
@@ -651,9 +734,17 @@ const draw = new Vue({
|
||||
}
|
||||
},
|
||||
// 画笔擦除
|
||||
drawDelete: function (options) {
|
||||
const { canvas, context, prePoint, curPoint, localDrawCallback } = options;
|
||||
context.lineWidth = this.lineWidth;
|
||||
drawDelete: function ({ event, drawMode, local : {
|
||||
canvas, context, lineWidth, curPoint, prePoint, lineCap,
|
||||
width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle
|
||||
}}) {
|
||||
context.lineWidth = lineWidth;
|
||||
context.lineCap = lineCap;
|
||||
context.lineJoin = lineJoin;
|
||||
context.strokeStyle = strokeStyle;
|
||||
context.fillStyle = fillStyle;
|
||||
|
||||
//防止移动过快,canvas渲染存在间隔,导致线条断层,在这里补充绘制间隔的点
|
||||
let x = prePoint.x;
|
||||
let y = prePoint.y;
|
||||
@@ -666,14 +757,17 @@ const draw = new Vue({
|
||||
while (i < distance) {
|
||||
x += xUnit;
|
||||
y += yUnit;
|
||||
context.clearRect(x - 20, y - 20, this.lineWidth, this.lineWidth);
|
||||
context.clearRect(x - 20, y - 20, lineWidth, lineWidth);
|
||||
i++;
|
||||
}
|
||||
},
|
||||
// 图片渲染处理
|
||||
drawImage: function (options) {
|
||||
drawImage: function ({ event, drawMode, local : {
|
||||
canvas, context, lineWidth, curPoint, prePoint, lineCap,
|
||||
width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
}}) {
|
||||
let that = this;
|
||||
const { canvas, context, localDrawCallback } = options;
|
||||
let input = document.createElement("input");
|
||||
input.setAttribute("type", "file");
|
||||
input.setAttribute("accept", "image/*");
|
||||
@@ -695,11 +789,14 @@ const draw = new Vue({
|
||||
}
|
||||
},
|
||||
// 画笔渲染处理, 两点式绘制
|
||||
drawLine: function (options) {
|
||||
const {
|
||||
canvas, context, localDrawCallback, prePoint, curPoint,
|
||||
lineWidth, strokeStyle, fillStyle, lineCap, lineJoin, fromRemote,
|
||||
} = options;
|
||||
drawLine: function ({ event, drawMode, fromRemote, local, remote}) {
|
||||
if(fromRemote){
|
||||
local = remote;
|
||||
}
|
||||
let {
|
||||
canvas, context, lineWidth, curPoint, prePoint, lineCap,
|
||||
width, height, devicePixelRatio, lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
} = local;
|
||||
|
||||
// 设置画笔样式
|
||||
context.lineWidth = lineWidth;
|
||||
@@ -728,16 +825,23 @@ const draw = new Vue({
|
||||
i++;
|
||||
}
|
||||
|
||||
if (!fromRemote) { //本地的绘制数据回调给远端
|
||||
localDrawCallback && localDrawCallback(options);
|
||||
//如果是本地绘制,完成绘制后数据回调给远端
|
||||
if (!fromRemote) {
|
||||
localDrawCallback && localDrawCallback({ event, drawMode, fromRemote : true, remote : {
|
||||
lineWidth, curPoint, prePoint, lineCap, width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle,
|
||||
}});
|
||||
}
|
||||
},
|
||||
// 星星渲染处理
|
||||
drawStar: function (options) {
|
||||
const {
|
||||
canvas, context, localDrawCallback, starStartPoint, curPoint,
|
||||
lineWidth, strokeStyle, fillStyle, lineCap, lineJoin, fromRemote, starFill
|
||||
} = options;
|
||||
drawStar: function ({ event, drawMode, fromRemote, local, remote}) {
|
||||
if(fromRemote){
|
||||
local = remote;
|
||||
}
|
||||
let {
|
||||
canvas, context, lineWidth, curPoint, starStartPoint, lineCap, starFill,
|
||||
width, height, devicePixelRatio, lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
} = local;
|
||||
|
||||
// 设置画笔样式
|
||||
context.lineWidth = lineWidth;
|
||||
@@ -778,16 +882,23 @@ const draw = new Vue({
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromRemote) { //本地的绘制数据回调给远端
|
||||
localDrawCallback && localDrawCallback(options);
|
||||
//如果是本地绘制,完成绘制后数据回调给远端
|
||||
if (!fromRemote) {
|
||||
localDrawCallback && localDrawCallback({ event, drawMode, fromRemote : true, remote : {
|
||||
lineWidth, curPoint, starStartPoint, lineCap, width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle, starFill
|
||||
}});
|
||||
}
|
||||
},
|
||||
// 三角形渲染处理
|
||||
drawTriangle: function (options) {
|
||||
const {
|
||||
canvas, context, localDrawCallback, triangleStartPoint, curPoint,
|
||||
lineWidth, strokeStyle, fillStyle, lineCap, lineJoin, fromRemote, triangleFill
|
||||
} = options;
|
||||
drawTriangle: function ({ event, drawMode, fromRemote, local, remote }) {
|
||||
if(fromRemote){
|
||||
local = remote;
|
||||
}
|
||||
let {
|
||||
canvas, context, lineWidth, curPoint, triangleStartPoint, lineCap, triangleFill,
|
||||
width, height, devicePixelRatio, lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
} = local;
|
||||
|
||||
// 设置画笔样式
|
||||
context.lineWidth = lineWidth;
|
||||
@@ -827,16 +938,23 @@ const draw = new Vue({
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromRemote) { //本地的绘制数据回调给远端
|
||||
localDrawCallback && localDrawCallback(options);
|
||||
//如果是本地绘制,完成绘制后数据回调给远端
|
||||
if (!fromRemote) {
|
||||
localDrawCallback && localDrawCallback({ event, drawMode, fromRemote : true, remote : {
|
||||
lineWidth, curPoint, triangleStartPoint, lineCap, width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle, triangleFill
|
||||
}});
|
||||
}
|
||||
},
|
||||
// 圆形渲染处理
|
||||
drawCircle: function (options) {
|
||||
const {
|
||||
canvas, context, localDrawCallback, circleStartPoint, curPoint,
|
||||
lineWidth, strokeStyle, fillStyle, lineCap, lineJoin, fromRemote, circleFill
|
||||
} = options;
|
||||
drawCircle: function ({ event, drawMode, fromRemote, local, remote }) {
|
||||
if(fromRemote){
|
||||
local = remote;
|
||||
}
|
||||
let {
|
||||
canvas, context, lineWidth, curPoint, circleStartPoint, lineCap, circleFill,
|
||||
width, height, devicePixelRatio, lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
} = local;
|
||||
|
||||
// 设置画笔样式
|
||||
context.lineWidth = lineWidth;
|
||||
@@ -869,16 +987,23 @@ const draw = new Vue({
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromRemote) { //本地的绘制数据回调给远端
|
||||
localDrawCallback && localDrawCallback(options);
|
||||
//如果是本地绘制,完成绘制后数据回调给远端
|
||||
if (!fromRemote) {
|
||||
localDrawCallback && localDrawCallback({ event, drawMode, fromRemote : true, remote : {
|
||||
lineWidth, curPoint, circleStartPoint, lineCap, width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle, circleFill
|
||||
}});
|
||||
}
|
||||
},
|
||||
// 矩形渲染处理
|
||||
drawRectangle: function (options) {
|
||||
const {
|
||||
canvas, context, localDrawCallback, rectangleStartPoint, curPoint,
|
||||
lineWidth, strokeStyle, fillStyle, lineCap, lineJoin, fromRemote, rectangleFill
|
||||
} = options;
|
||||
drawRectangle: function ({ event, drawMode, fromRemote, local, remote }) {
|
||||
if(fromRemote){
|
||||
local = remote;
|
||||
}
|
||||
let {
|
||||
canvas, context, lineWidth, curPoint, rectangleStartPoint, lineCap, rectangleFill,
|
||||
width, height, devicePixelRatio, lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
} = local;
|
||||
|
||||
// 设置画笔样式
|
||||
context.lineWidth = lineWidth;
|
||||
@@ -890,8 +1015,8 @@ const draw = new Vue({
|
||||
// 计算矩形的位置和尺寸
|
||||
const x = Math.min(rectangleStartPoint.x, curPoint.x);
|
||||
const y = Math.min(rectangleStartPoint.y, curPoint.y);
|
||||
const width = Math.abs(curPoint.x - rectangleStartPoint.x);
|
||||
const height = Math.abs(curPoint.y - rectangleStartPoint.y);
|
||||
const rwidth = Math.abs(curPoint.x - rectangleStartPoint.x);
|
||||
const rheight = Math.abs(curPoint.y - rectangleStartPoint.y);
|
||||
|
||||
if (this.drawRollbackPoint >= 0) {
|
||||
const img = new Image();
|
||||
@@ -903,7 +1028,7 @@ const draw = new Vue({
|
||||
context.drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||
//绘制新矩形
|
||||
context.beginPath();
|
||||
context.rect(x, y, width, height);
|
||||
context.rect(x, y, rwidth, rheight);
|
||||
if(rectangleFill){
|
||||
context.fill();
|
||||
}
|
||||
@@ -911,43 +1036,58 @@ const draw = new Vue({
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromRemote) { //本地的绘制数据回调给远端
|
||||
localDrawCallback && localDrawCallback(options);
|
||||
//如果是本地绘制,完成绘制后数据回调给远端
|
||||
if (!fromRemote) {
|
||||
localDrawCallback && localDrawCallback({ event, drawMode, fromRemote : true, remote : {
|
||||
lineWidth, curPoint, rectangleStartPoint, lineCap, width, height, devicePixelRatio,
|
||||
lineJoin, strokeStyle, fillStyle, rectangleFill
|
||||
}});
|
||||
}
|
||||
},
|
||||
// 文字渲染处理
|
||||
drawText: function (options) {
|
||||
let {
|
||||
canvas, context, localDrawCallback, curPoint, text, lineWidth, strokeStyle,
|
||||
fillStyle, lineCap, lineJoin, fromRemote,
|
||||
} = options;
|
||||
drawText: function ({ event, drawMode, fromRemote, local, remote }) {
|
||||
if(fromRemote){
|
||||
local = remote;
|
||||
}
|
||||
let {
|
||||
canvas, context, lineWidth, curPoint, text, lineCap,
|
||||
width, height, devicePixelRatio, lineJoin, strokeStyle, fillStyle, localDrawCallback
|
||||
} = local;
|
||||
|
||||
curPoint = {
|
||||
x : curPoint.x / window.devicePixelRatio,
|
||||
y : curPoint.y / window.devicePixelRatio
|
||||
x : curPoint.x / devicePixelRatio,
|
||||
y : curPoint.y / devicePixelRatio
|
||||
}
|
||||
|
||||
// 设置字体样式
|
||||
context.strokeStyle = strokeStyle;
|
||||
context.font = "28px orbitron";
|
||||
context.font = Math.min(lineWidth * 7, 28) + "px orbitron";
|
||||
context.textBaseline = "middle";
|
||||
context.lineCap = lineCap;
|
||||
context.lineJoin = lineJoin;
|
||||
context.lineWidth = 3;
|
||||
context.lineWidth = 1;
|
||||
|
||||
const canvasWidth = parseInt(canvas.style.width);
|
||||
const canvasHeight = parseInt(canvas.style.height);
|
||||
|
||||
//文字渲染处理
|
||||
function drawTextHandler(content){
|
||||
const textWidth = context.measureText(content).width;
|
||||
// 如果文字超出画布宽度,将文字绘制到画布最右边
|
||||
let fixPointX = canvasWidth - textWidth > curPoint.x ? curPoint.x : canvasWidth - textWidth;
|
||||
fixPointX = fixPointX < 10 ? 10 : fixPointX;
|
||||
// 如果文字超出画布高度,将文字绘制到画布最下边
|
||||
let fixPointY = canvasHeight - 20 > curPoint.y ? curPoint.y : canvasHeight - 20;
|
||||
fixPointY = fixPointY < 10 ? 10 : fixPointY;
|
||||
context.strokeText(content, fixPointX * window.devicePixelRatio, fixPointY * window.devicePixelRatio);
|
||||
let words = content.split("");
|
||||
let subWords = "";
|
||||
let wordHeight = 40;
|
||||
for(let i = 0; i < words.length; i++){
|
||||
let curSubWords = subWords + words[i];
|
||||
const curSubWordsWidth = context.measureText(curSubWords).width;
|
||||
|
||||
if(curPoint.x + curSubWordsWidth > canvasWidth && i > 0){
|
||||
context.fillText(subWords, curPoint.x * devicePixelRatio, curPoint.y * devicePixelRatio);
|
||||
subWords = words[i];
|
||||
curPoint.y += wordHeight;
|
||||
}else{
|
||||
subWords = curSubWords;
|
||||
}
|
||||
}
|
||||
context.fillText(subWords, curPoint.x * devicePixelRatio, curPoint.y * devicePixelRatio);
|
||||
}
|
||||
|
||||
if(fromRemote){
|
||||
@@ -983,7 +1123,7 @@ const draw = new Vue({
|
||||
} else if (curPoint.y < 100) {
|
||||
textarea.style.bottom = (canvasHeight - 100) + 'px';
|
||||
} else {
|
||||
textarea.style.top = (curPoint.y + 70)+ 'px';
|
||||
textarea.style.top = (curPoint.y + 170)+ 'px';
|
||||
}
|
||||
parentDom.appendChild(textarea);
|
||||
textarea.focus();
|
||||
@@ -992,8 +1132,11 @@ const draw = new Vue({
|
||||
if (textarea.value !== '') {
|
||||
drawTextHandler(textarea.value);
|
||||
document.getElementById("drawLine").click()
|
||||
options.text = textarea.value;
|
||||
localDrawCallback && localDrawCallback(options);
|
||||
|
||||
localDrawCallback && localDrawCallback({ event, drawMode, fromRemote : true, remote : {
|
||||
lineWidth, curPoint, lineCap, width, height, devicePixelRatio, text : textarea.value,
|
||||
lineJoin, strokeStyle, fillStyle,
|
||||
}});
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -204,8 +204,102 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateRemoteRtcState : async function(){
|
||||
for(let id in this.remoteMap){
|
||||
let stat = await window.tlrtcfile.getWebrtcStats(
|
||||
this.getOrCreateRtcConnect(id)
|
||||
);
|
||||
let remoteCandidate = stat.get("remote-candidate") || [];
|
||||
let p2pModes = remoteCandidate.map(item => {
|
||||
if(['host','srflx','prflx'].includes(item.report.candidateType)){
|
||||
return "直连"
|
||||
}else if(item.report.candidateType === 'relay'){
|
||||
return "中继"
|
||||
}else{
|
||||
return "未知"
|
||||
}
|
||||
})
|
||||
this.setRemoteInfo(id, {
|
||||
//p2p连接模式: host, srflx, prflx, relay
|
||||
p2pMode : Array.from(new Set(p2pModes)).join(",")
|
||||
})
|
||||
}
|
||||
this.$forceUpdate()
|
||||
},
|
||||
showRemoteUser : async function(remote){
|
||||
let stat = await window.tlrtcfile.getWebrtcStats(this.getOrCreateRtcConnect(remote.id));
|
||||
const rtcStatList = [];
|
||||
stat.forEach((value, key)=>{
|
||||
rtcStatList.push(
|
||||
...value.map(item => {
|
||||
return Object.assign(item.report, {
|
||||
description_zh: item.description
|
||||
})
|
||||
})
|
||||
)
|
||||
})
|
||||
let rtcStatDomList = '';
|
||||
rtcStatList.forEach(statItem => {
|
||||
let rtcStatDomVal = ` <div> description_zh: <b>${statItem.description_zh}</b> </div> `;
|
||||
for(let key in statItem){
|
||||
if(key === 'description_zh'){
|
||||
continue
|
||||
}
|
||||
rtcStatDomVal += ` <div> ${key}: <b>${statItem[key]}</b> </div> `;
|
||||
}
|
||||
rtcStatDomList += ` <div> ${rtcStatDomVal} </div> `;
|
||||
})
|
||||
|
||||
let that = this;
|
||||
layer.closeAll(function () {
|
||||
layer.open({
|
||||
type: 1,
|
||||
closeBtn: 0,
|
||||
fixed: true,
|
||||
maxmin: false,
|
||||
shadeClose: true,
|
||||
area: ['350px', '380px'],
|
||||
title: `rtc连接实时统计信息`,
|
||||
success: function (layero, index) {
|
||||
document.querySelector(".layui-layer-title").style.borderTopRightRadius = "8px";
|
||||
document.querySelector(".layui-layer-title").style.borderTopLeftRadius = "8px";
|
||||
document.querySelector(".layui-layer").style.borderRadius = "8px";
|
||||
document.querySelector(".layui-layer").style.background = "#f8f8f8";
|
||||
|
||||
carousel.render({
|
||||
elem: '#tl-rtc-file-rtcinfo',
|
||||
width: '100%',
|
||||
autoplay : false,
|
||||
indicator: 'outside'
|
||||
});
|
||||
},
|
||||
content: `
|
||||
<div class="remote_user_info layui-carousel" id="tl-rtc-file-rtcinfo">
|
||||
<div carousel-item>
|
||||
<div>
|
||||
<div> ${that.lang.userid}: <b>${remote.id}</b> </div>
|
||||
<div> ${that.lang.nickname}: <b>${remote.nickName}</b> </div>
|
||||
<div> ${that.lang.room_channel}: <b>${that.roomId}</b> </div>
|
||||
<div> ${that.lang.website_language}: <b>${remote.langMode}</b> </div>
|
||||
<div> ${that.lang.network_status}: <b>${remote.network}</b> </div>
|
||||
<div> ${that.lang.join_time}: <b>${remote.joinTime}</b> </div>
|
||||
<div> ${that.lang.public_ip}: <b>${remote.ip}</b> </div>
|
||||
<div> ${that.lang.webrtc_ice_state}: <b>${remote.iceConnectionState}</b> </div>
|
||||
<div> ${that.lang.device_classification}: <b>${remote.ua}</b> </div>
|
||||
<div> ${that.lang.terminal_equipment}: <b>${remote.userAgent}</b> </div>
|
||||
</div>
|
||||
${rtcStatDomList}
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
})
|
||||
},
|
||||
iceOk : function(state){
|
||||
return ['completed', 'connected', 'checking', 'new'].includes(state);
|
||||
},
|
||||
consoleLogo : function(){
|
||||
window.console.log(`%c____ TL-RTC-FILE-V10.1.5 ____ \n____ FORK ME IN GITHUB ____ \n____ https://github.com/tl-open-source/tl-rtc-file ____`, this.logo)
|
||||
window.console.log(`%c____ TL-RTC-FILE-V${this.version} ____ \n____ FORK ME IN GITHUB ____ \n____ https://github.com/tl-open-source/tl-rtc-file ____`, this.logo)
|
||||
},
|
||||
changeLanguage: function () {
|
||||
let that = this;
|
||||
@@ -291,9 +385,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
let file = filterFile[0]
|
||||
|
||||
if (file.size > this.uploadCodeFileMaxSize) {
|
||||
if(window.layer){
|
||||
layer.msg(`${this.lang.max_saved} ${this.uploadCodeFileMaxSize / 1024 / 1024} ${this.lang.mb_file}`);
|
||||
}
|
||||
layer.msg(`${this.lang.max_saved} ${this.uploadCodeFileMaxSize / 1024 / 1024} ${this.lang.mb_file}`);
|
||||
return
|
||||
}
|
||||
|
||||
@@ -363,7 +455,8 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.initSendFile(recoder);
|
||||
},
|
||||
// 私聊弹窗
|
||||
startChatRoomSingle: function(remote){
|
||||
startChatRoomSingle: function(event, remote){
|
||||
event.stopPropagation();
|
||||
this.chatRoomSingleSocketId = remote.id;
|
||||
let that = this;
|
||||
let options = {
|
||||
@@ -433,7 +526,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
</div>
|
||||
<div class="chating_input_body">
|
||||
<textarea maxlength="50000" id="chating_room_single_value" class="layui-textarea" placeholder="${this.lang.communication_rational} ~"></textarea>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter ${this.lang.enter_send} </span>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter | ${this.lang.enter_send} </span>
|
||||
<button onclick="sendChatingRoomSingle()" type="button" class="layui-btn layui-btn-normal layui-btn-sm chating_send_body chating_send_body_button">${this.lang.send_chat}</button>
|
||||
</div>
|
||||
`
|
||||
@@ -626,7 +719,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
let fileRecorde = filterFile[0];
|
||||
if (fileRecorde.size > this.previewFileMaxSize) {
|
||||
layer.msg(`${this.lang.max_previewed} ${this.previewFileMaxSize / 1024 / 1024} ${mb_file}`);
|
||||
layer.msg(`${this.lang.max_previewed} ${this.previewFileMaxSize / 1024 / 1024} ${this.lang.mb_file}`);
|
||||
return
|
||||
}
|
||||
|
||||
@@ -853,7 +946,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
<div class="chating_input_body">
|
||||
<textarea maxlength="50000" id="openaiChat_value" class="layui-textarea" placeholder="${this.lang.communication_rational} ~"></textarea>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter ${this.lang.enter_send} </span>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter | ${this.lang.enter_send} </span>
|
||||
<button onclick="sendOpenaiChat()" type="button" class="layui-btn layui-btn-normal layui-btn-sm chating_send_body chating_send_body_button">${this.lang.send_chat}</button>
|
||||
</div>
|
||||
`
|
||||
@@ -1007,11 +1100,11 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
title: this.lang.please_enter_password
|
||||
title: that.lang.please_enter_password
|
||||
}, function (value, index, elem) {
|
||||
that.createPasswordRoom(value);
|
||||
layer.close(index);
|
||||
that.addUserLogs(this.lang.enter_password_room + that.roomId + `,${this.lang.password}:` + value);
|
||||
that.addUserLogs(that.lang.enter_password_room + that.roomId + `,${that.lang.password}:` + value);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1124,12 +1217,12 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
<div class="setting-main">
|
||||
<div class="setting-main-body">
|
||||
<div class="relayDoc" style="padding: 15px; position: absolute; width: 100%; height: 100%;">
|
||||
<p style="text-align: center; font-weight: bold; position: relative; top: 2px; display: block; font-size: 17px;"> ${this.lang.relay_server_current} ${useTurn ? this.lang.on : this.lang.off} </p>
|
||||
<p style="font-weight: bold; position: relative; top: 15px; display: block; font-size: 14px;"> ${this.lang.relay_server_current_detail} </p>
|
||||
<div style="position: relative; margin-top: 140px;">
|
||||
<p style="text-align: center; font-weight: bold; position: relative; top: 2px; display: block; font-size: 17px;"> ${this.lang.relay_server_current} '${useTurn ? this.lang.on : this.lang.off}' </p>
|
||||
<p style="font-weight: bold; position: relative; top: 15px; display: block; font-size: 14px;height: 80%;"> ${this.lang.relay_server_current_detail} </p>
|
||||
<div>
|
||||
<div style="text-align: center;">
|
||||
<button onclick="useTurn()" type="button" class="layui-btn layui-btn-sm layui-btn-normal" style="margin-right: 45px;"> ${this.lang.on} </button>
|
||||
<button onclick="useTurn()" type="button" class="layui-btn layui-btn-sm layui-btn-danger"> ${this.lang.off} </button>
|
||||
<button onclick="useTurn()" type="button" class="layui-btn layui-btn-sm layui-btn-primary layui-border-blue" style="margin-right: 45px;"> ${this.lang.on} </button>
|
||||
<button onclick="useTurn()" type="button" class="layui-btn layui-btn-sm layui-btn-primary layui-border-red"> ${this.lang.off} </button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1180,11 +1273,11 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
that.clickMediaVideo();
|
||||
that.isVideoShare = !that.isVideoShare;
|
||||
that.addUserLogs(this.lang.start_video_call);
|
||||
that.addUserLogs(that.lang.start_video_call);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
title: this.lang.please_enter_video_call_room_num
|
||||
title: that.lang.please_enter_video_call_room_num
|
||||
}, function (value, index, elem) {
|
||||
that.roomId = value;
|
||||
that.createMediaRoom("video");
|
||||
@@ -1197,7 +1290,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
that.clickMediaVideo();
|
||||
that.isVideoShare = !that.isVideoShare;
|
||||
that.addUserLogs(this.lang.start_video_call);
|
||||
that.addUserLogs(that.lang.start_video_call);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -1239,7 +1332,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
that.clickMediaScreen();
|
||||
that.isScreenShare = !that.isScreenShare;
|
||||
that.addUserLogs(this.lang.start_screen_sharing);
|
||||
that.addUserLogs(that.lang.start_screen_sharing);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
@@ -1256,7 +1349,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
that.clickMediaScreen();
|
||||
that.isScreenShare = !that.isScreenShare;
|
||||
that.addUserLogs(this.lang.this.lang.start_screen_sharing);
|
||||
that.addUserLogs(that.lang.start_screen_sharing);
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -1289,9 +1382,25 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
return
|
||||
}
|
||||
let that = this;
|
||||
if (window.layer) {
|
||||
if(that.isShareJoin){ //分享进入
|
||||
if(that.isShareJoin){ //分享进入
|
||||
that.createMediaRoom("live");
|
||||
that.socket.emit('message', {
|
||||
emitType: "startLiveShare",
|
||||
room: that.roomId,
|
||||
to : that.socketId
|
||||
});
|
||||
that.clickMediaLive();
|
||||
that.isLiveShare = !that.isLiveShare;
|
||||
that.addUserLogs(that.lang.start_live);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
title: this.lang.please_enter_live_room_num,
|
||||
}, function (value, index, elem) {
|
||||
that.roomId = value;
|
||||
that.createMediaRoom("live");
|
||||
layer.close(index)
|
||||
|
||||
that.socket.emit('message', {
|
||||
emitType: "startLiveShare",
|
||||
room: that.roomId,
|
||||
@@ -1299,30 +1408,13 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
});
|
||||
that.clickMediaLive();
|
||||
that.isLiveShare = !that.isLiveShare;
|
||||
that.addUserLogs(this.lang.start_live);
|
||||
}else{
|
||||
layer.prompt({
|
||||
formType: 1,
|
||||
title: this.lang.please_enter_live_room_num,
|
||||
}, function (value, index, elem) {
|
||||
that.roomId = value;
|
||||
that.createMediaRoom("live");
|
||||
layer.close(index)
|
||||
|
||||
that.socket.emit('message', {
|
||||
emitType: "startLiveShare",
|
||||
room: that.roomId,
|
||||
to : that.socketId
|
||||
});
|
||||
that.clickMediaLive();
|
||||
that.isLiveShare = !that.isLiveShare;
|
||||
that.addUserLogs(this.lang.start_live);
|
||||
});
|
||||
}
|
||||
that.addUserLogs(that.lang.start_live);
|
||||
});
|
||||
}
|
||||
},
|
||||
// 打开画笔
|
||||
openRemoteDraw : function(){
|
||||
let that = this;
|
||||
if (!this.switchData.openRemoteDraw) {
|
||||
layer.msg(this.lang.feature_close)
|
||||
this.addUserLogs(this.lang.feature_close)
|
||||
@@ -1338,26 +1430,26 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
// 触发draw.js中的方法
|
||||
window.Bus.$emit("openDraw", {
|
||||
openCallback: () => {
|
||||
this.socket.emit('message', {
|
||||
that.socket.emit('message', {
|
||||
emitType: "startRemoteDraw",
|
||||
room: this.roomId,
|
||||
to: this.socketId
|
||||
room: that.roomId,
|
||||
to: that.socketId
|
||||
});
|
||||
},
|
||||
closeCallback: (drawCount) => {
|
||||
this.socket.emit('message', {
|
||||
that.socket.emit('message', {
|
||||
emitType: "stopRemoteDraw",
|
||||
room: this.roomId,
|
||||
to: this.socketId,
|
||||
room: that.roomId,
|
||||
to: that.socketId,
|
||||
drawCount : drawCount
|
||||
});
|
||||
},
|
||||
localDrawCallback : (data) => {
|
||||
Object.entries(this.remoteMap).forEach(([id, remote]) => {
|
||||
Object.entries(that.remoteMap).forEach(([id, remote]) => {
|
||||
if(remote && remote.sendDataChannel){
|
||||
const sendDataChannel = remote.sendDataChannel;
|
||||
if (!sendDataChannel || sendDataChannel.readyState !== 'open') {
|
||||
this.addSysLogs("sendDataChannel error in draw")
|
||||
that.addSysLogs("sendDataChannel error in draw")
|
||||
return;
|
||||
}
|
||||
sendDataChannel.send(JSON.stringify(data));
|
||||
@@ -1387,15 +1479,15 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
openCallback : () => {
|
||||
that.socket.emit('message', {
|
||||
emitType: "startScreen",
|
||||
room: this.roomId,
|
||||
to : this.socketId
|
||||
room: that.roomId,
|
||||
to : that.socketId
|
||||
});
|
||||
that.addUserLogs(this.lang.start_local_screen_recording);
|
||||
that.addUserLogs(that.lang.start_local_screen_recording);
|
||||
},
|
||||
closeCallback : (res) => {
|
||||
this.receiveFileRecoderList.push({
|
||||
id: this.lang.web_screen_recording,
|
||||
nickName : this.nickName,
|
||||
that.receiveFileRecoderList.push({
|
||||
id: that.lang.web_screen_recording,
|
||||
nickName : that.nickName,
|
||||
href: res.src,
|
||||
style: 'color: #ff5722;text-decoration: underline;',
|
||||
name: 'screen-recording-' + res.donwId + '.mp4',
|
||||
@@ -1406,14 +1498,14 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
start: 0,
|
||||
cost: res.times
|
||||
})
|
||||
this.socket.emit('message', {
|
||||
that.socket.emit('message', {
|
||||
emitType: "stopScreen",
|
||||
to : this.socketId,
|
||||
room: this.roomId,
|
||||
to : that.socketId,
|
||||
room: that.roomId,
|
||||
size: res.size,
|
||||
cost: res.times
|
||||
});
|
||||
this.addUserLogs(this.lang.end_local_screen_recording);
|
||||
that.addUserLogs(that.lang.end_local_screen_recording);
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -1461,7 +1553,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
</div>
|
||||
<div class="chating_input_body">
|
||||
<textarea maxlength="50000" id="chating_comm_value" class="layui-textarea" placeholder="${this.lang.communication_rational} ~"></textarea>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter ${this.lang.enter_send} </span>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter | ${this.lang.enter_send} </span>
|
||||
<button onclick="sendChatingComm()" type="button" class="layui-btn layui-btn-normal layui-btn-sm chating_send_body chating_send_body_button">${this.lang.send_chat}</button>
|
||||
</div>
|
||||
`
|
||||
@@ -1617,7 +1709,7 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
</div>
|
||||
<div class="chating_input_body">
|
||||
<textarea maxlength="50000" id="chating_room_value" class="layui-textarea" placeholder="${this.lang.communication_rational} ~"></textarea>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter ${this.lang.enter_send} </span>
|
||||
<span class="chating_send_body chating_send_body_span">shift+enter | ${this.lang.enter_send} </span>
|
||||
<button onclick="sendChatingRoom()" type="button" class="layui-btn layui-btn-normal layui-btn-sm chating_send_body chating_send_body_button">${this.lang.send_chat}</button>
|
||||
</div>
|
||||
`
|
||||
@@ -2118,7 +2210,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
type : 'password',
|
||||
password : '',
|
||||
nickName : this.nickName,
|
||||
langMode : this.langMode
|
||||
langMode : this.langMode,
|
||||
ua: this.isMobile ? 'mobile' : 'pc',
|
||||
network : this.network
|
||||
});
|
||||
this.isJoined = true;
|
||||
this.addPopup({
|
||||
@@ -2147,7 +2241,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
room: this.roomId,
|
||||
type: type,
|
||||
nickName : this.nickName,
|
||||
langMode : this.langMode
|
||||
langMode : this.langMode,
|
||||
ua: this.isMobile ? 'mobile' : 'pc',
|
||||
network : this.network
|
||||
});
|
||||
this.isJoined = true;
|
||||
this.roomType = type;
|
||||
@@ -2183,7 +2279,9 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
type : 'password',
|
||||
password: password,
|
||||
nickName : this.nickName,
|
||||
langMode : this.langMode
|
||||
langMode : this.langMode,
|
||||
ua: this.isMobile ? 'mobile' : 'pc',
|
||||
network : this.network
|
||||
});
|
||||
this.isJoined = true;
|
||||
this.addPopup({
|
||||
@@ -2225,12 +2323,19 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
rtcConnect.oniceconnectionstatechange = (e) => {
|
||||
that.addSysLogs("iceConnectionState: " + rtcConnect.iceConnectionState);
|
||||
that.setRemoteInfo(id, {
|
||||
iceConnectionState : rtcConnect.iceConnectionState
|
||||
})
|
||||
}
|
||||
|
||||
//保存peer连接
|
||||
this.rtcConns[id] = rtcConnect;
|
||||
if (!this.remoteMap[id]) {
|
||||
Vue.set(this.remoteMap, id, { id: id, receiveChatRoomSingleList : [] })
|
||||
Vue.set(this.remoteMap, id, {
|
||||
id: id,
|
||||
receiveChatRoomSingleList : [],
|
||||
p2pMode : '识别中...'
|
||||
})
|
||||
}
|
||||
|
||||
//数据通道
|
||||
@@ -2467,11 +2572,20 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
})
|
||||
}
|
||||
|
||||
// 缓冲区満了
|
||||
//缓冲区暂定 256kb
|
||||
sendFileDataChannel.bufferedAmountLowThreshold = 16 * 1024 * 16;
|
||||
//局域网一般不会走缓冲区,所以bufferedAmount一般为0,公网部分情况受限于带宽,bufferedAmount可能会逐渐堆积,从而进行排队
|
||||
if (sendFileDataChannel.bufferedAmount > sendFileDataChannel.bufferedAmountLowThreshold) {
|
||||
this.addSysLogs(this.lang.file_send_channel_buffer_full)
|
||||
this.addSysLogs(
|
||||
that.lang.file_send_channel_buffer_full + ",bufferedAmount=" +
|
||||
sendFileDataChannel.bufferedAmount + ",bufferedAmountLowThreshold=" +
|
||||
sendFileDataChannel.bufferedAmountLowThreshold
|
||||
)
|
||||
sendFileDataChannel.onbufferedamountlow = () => {
|
||||
this.addSysLogs(this.lang.file_send_channel_buffer_recover)
|
||||
that.addSysLogs(
|
||||
that.lang.file_send_channel_buffer_recover + ",bufferedAmount=" +
|
||||
sendFileDataChannel.bufferedAmount
|
||||
)
|
||||
sendFileDataChannel.onbufferedamountlow = null;
|
||||
that.sendFileToRemoteByLoop(event);
|
||||
}
|
||||
@@ -2844,15 +2958,17 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
|
||||
for (let i = 0; i < data.peers.length; i++) {
|
||||
let otherSocketId = data.peers[i].id;
|
||||
let otherSocketIdNickName = data.peers[i].nickName;
|
||||
let otherSocketIdLangMode = data.peers[i].langMode;
|
||||
let otherSocketIdOwner = data.peers[i].owner;
|
||||
let rtcConnect = that.getOrCreateRtcConnect(otherSocketId);
|
||||
// 处理完连接后,更新下昵称
|
||||
that.setRemoteInfo(otherSocketId, {
|
||||
nickName : otherSocketIdNickName,
|
||||
langMode : otherSocketIdLangMode,
|
||||
owner : otherSocketIdOwner
|
||||
nickName : data.peers[i].nickName,
|
||||
langMode : data.peers[i].langMode,
|
||||
owner : data.peers[i].owner,
|
||||
ua : data.peers[i].ua,
|
||||
joinTime : data.peers[i].joinTime,
|
||||
userAgent : data.peers[i].userAgent,
|
||||
ip : data.peers[i].ip,
|
||||
network : data.peers[i].network,
|
||||
})
|
||||
|
||||
await new Promise(resolve => {
|
||||
@@ -2895,7 +3011,11 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
nickName : data.nickName,
|
||||
owner : data.owner,
|
||||
langMode : data.langMode,
|
||||
owner : false
|
||||
ua : data.ua,
|
||||
network : data.network,
|
||||
joinTime : data.joinTime,
|
||||
userAgent : data.userAgent,
|
||||
ip : data.ip,
|
||||
})
|
||||
// 处理音视频逻辑
|
||||
if (data.type === 'screen') {
|
||||
@@ -3656,10 +3776,14 @@ axios.get("/api/comm/initData?turn="+useTurn, {}).then((initData) => {
|
||||
this.addSysLogs(this.lang.basic_data_get_done);
|
||||
|
||||
this.addSysLogs(this.lang.window_event_init);
|
||||
window.onresize = this.touchResize;
|
||||
window.onresize = this.touchResize;
|
||||
setInterval(() => {
|
||||
this.touchResize()
|
||||
}, 1000);
|
||||
|
||||
setInterval(async () => {
|
||||
await this.updateRemoteRtcState()
|
||||
}, 5000);
|
||||
this.addSysLogs(this.lang.window_event_init_done);
|
||||
|
||||
this.addSysLogs(this.lang.message_box_init);
|
||||
|
@@ -314,8 +314,28 @@ const local_lang = {
|
||||
"you_refresh_room": "You refreshed the room number, the current room number is",
|
||||
"your_browser": "Your browser",
|
||||
"your_ip_list": "Your IP list is",
|
||||
"nickname" : "Nickname",
|
||||
"userid" : "Userd",
|
||||
"room_channel" : "Room channel",
|
||||
"join_time" : "Join time",
|
||||
"website_language" : "Website language",
|
||||
"terminal_equipment" : "Terminal equipment",
|
||||
"device_classification" : "Device classification",
|
||||
"network_status" : "Network status",
|
||||
"public_ip" : "Public IP",
|
||||
"webrtc_ice_state" : "webrtc state"
|
||||
},
|
||||
"zh": {
|
||||
"webrtc_ice_state" : "webrtc状态",
|
||||
"nickname" : "用户昵称",
|
||||
"userid" : "用户ID ",
|
||||
"room_channel" : "房间频道",
|
||||
"join_time" : "加入时间",
|
||||
"website_language" : "网站语言",
|
||||
"terminal_equipment" : "终端设备",
|
||||
"device_classification" : "设备分类",
|
||||
"network_status" : "网络状态",
|
||||
"public_ip" : "传输地址",
|
||||
"add_ice_candidate_failed": "addIceCandidate失败",
|
||||
"add_ice_candidate_success": "addIceCandidateSuccess成功",
|
||||
"ai_answering": "AI正在回答您的问题,请稍后再问",
|
||||
@@ -445,9 +465,9 @@ const local_lang = {
|
||||
"not_support": "不支持",
|
||||
"note_website_for_learing": "注意:示例网站仅用于学习演示,请勿他用",
|
||||
"notice": "通知",
|
||||
"off": "已关闭",
|
||||
"off": "关闭",
|
||||
"offer_failed": "offer失败",
|
||||
"on": "已开启",
|
||||
"on": "开启",
|
||||
"online": "当前在线人数",
|
||||
"online_number": "人在线",
|
||||
"only_show" : "仅展示 ",
|
||||
@@ -553,7 +573,7 @@ const local_lang = {
|
||||
"select_wait_send_record": "选择待发送记录中",
|
||||
"selected_file": "已选择文件",
|
||||
"selected_file_exist": "选择的文件已经存在相同的文件,不再重复添加",
|
||||
"self": "我自己",
|
||||
"self": "自己",
|
||||
"send": "发送",
|
||||
"send_all": "一键发送",
|
||||
"send_alone": "单独发送",
|
||||
|
@@ -63,7 +63,7 @@
|
||||
|
||||
<h2>收费模式</h2>
|
||||
<ul>
|
||||
<li><strong>按小时计费:</strong>根据实际工作时间计算费用,目前个人定制功能2小时起,每小时200~300,企业定制另谈</li>
|
||||
<li><strong>按小时计费:</strong>根据实际工作时间计算费用,目前个人定制功能2小时起,每小时200~300</li>
|
||||
<li><strong>阶段性付费:</strong>确认开发前,预付1/3,开发完成付1/3,交付上线1/3</li>
|
||||
</ul>
|
||||
|
||||
|
@@ -19,7 +19,10 @@ const check = require("./../../utils/check/content");
|
||||
async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
let {handshake, userAgent, ip} = utils.getSocketClientInfo(socket);
|
||||
|
||||
let {room, type, nickName, password = '', langMode = 'zh'} = data;
|
||||
let {
|
||||
room, type = 'file', nickName = '', password = '',
|
||||
langMode = 'zh', ua = '', network = ''
|
||||
} = data;
|
||||
|
||||
if (room && room.length > 15) {
|
||||
room = room.toString().substr(0, 14);
|
||||
@@ -28,14 +31,37 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
if(nickName && nickName.length > 20){
|
||||
nickName = nickName.substr(0, 20);
|
||||
}
|
||||
//设置昵称
|
||||
io.sockets.connected[socket.id].nickName = nickName;
|
||||
io.sockets.connected[socket.id].langMode = langMode;
|
||||
|
||||
if(['zh', 'en'].indexOf(langMode) === -1){
|
||||
langMode = 'zh'
|
||||
}
|
||||
|
||||
if(['file', 'screen', 'video', 'password', 'live'].indexOf(type) === -1){
|
||||
type = 'file'
|
||||
}
|
||||
|
||||
if(['pc', 'mobile'].indexOf(ua) === -1){
|
||||
ua = 'pc';
|
||||
}
|
||||
|
||||
if(['wifi', '4g', '3g', '2g', '5g'].indexOf(network) === -1){
|
||||
network = '2g';
|
||||
}
|
||||
|
||||
if(password && password.length > 6){
|
||||
password = password.toString().substr(0,6);
|
||||
}
|
||||
|
||||
//设置连接信息
|
||||
io.sockets.connected[socket.id].nickName = nickName;
|
||||
io.sockets.connected[socket.id].langMode = langMode;
|
||||
io.sockets.connected[socket.id].ua = ua;
|
||||
io.sockets.connected[socket.id].network = network;
|
||||
io.sockets.connected[socket.id].ip = ip;
|
||||
const joinTime = utils.formateDateTime(new Date(), "yyyy-MM-dd hh:mm:ss")
|
||||
io.sockets.connected[socket.id].joinTime = joinTime;
|
||||
io.sockets.connected[socket.id].userAgent = userAgent;
|
||||
|
||||
let recoderId = await daoRoom.createJoinRoom({
|
||||
uid: "1",
|
||||
uname: nickName,
|
||||
@@ -105,6 +131,11 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
type: type,
|
||||
recoderId : recoderId,
|
||||
langMode : langMode,
|
||||
ua : ua,
|
||||
network : network,
|
||||
joinTime : joinTime,
|
||||
ip : ip,
|
||||
userAgent : userAgent
|
||||
});
|
||||
|
||||
let peers = new Array();
|
||||
@@ -114,11 +145,22 @@ async function userCreateAndJoin(io, socket, tables, dbClient, data){
|
||||
let peerNickName = io.sockets.connected[otherSocketId].nickName
|
||||
let peerOwner = io.sockets.connected[otherSocketId].owner
|
||||
let peerLangMode = io.sockets.connected[otherSocketId].langMode
|
||||
let peerUa = io.sockets.connected[otherSocketId].ua
|
||||
let peerNetwork = io.sockets.connected[otherSocketId].network
|
||||
let peerJoinTime = io.sockets.connected[otherSocketId].joinTime
|
||||
let peerIp = io.sockets.connected[otherSocketId].ip
|
||||
let peerUserAgent = io.sockets.connected[otherSocketId].userAgent
|
||||
|
||||
peers.push({
|
||||
id: otherSocketId,
|
||||
nickName: peerNickName,
|
||||
owner : peerOwner,
|
||||
langMode : peerLangMode
|
||||
langMode : peerLangMode,
|
||||
ua : peerUa,
|
||||
network : peerNetwork,
|
||||
joinTime : peerJoinTime,
|
||||
ip : peerIp,
|
||||
userAgent : peerUserAgent
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -54,6 +54,30 @@
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">数据汇总</div>
|
||||
<div class="code-name">&#xe61d;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">控桩终端</div>
|
||||
<div class="code-name">&#xe6bb;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">浏览器</div>
|
||||
<div class="code-name">&#xe6e8;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">连接流</div>
|
||||
<div class="code-name">&#xec57;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">三角形</div>
|
||||
@@ -534,9 +558,9 @@
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.woff2?t=1688291642630') format('woff2'),
|
||||
url('iconfont.woff?t=1688291642630') format('woff'),
|
||||
url('iconfont.ttf?t=1688291642630') format('truetype');
|
||||
src: url('iconfont.woff2?t=1688799262496') format('woff2'),
|
||||
url('iconfont.woff?t=1688799262496') format('woff'),
|
||||
url('iconfont.ttf?t=1688799262496') format('truetype');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
@@ -562,6 +586,42 @@
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-daohang-shujufenxi"></span>
|
||||
<div class="name">
|
||||
数据汇总
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-daohang-shujufenxi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-kongzhuangzhongduan"></span>
|
||||
<div class="name">
|
||||
控桩终端
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-kongzhuangzhongduan
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-liulanqi"></span>
|
||||
<div class="name">
|
||||
浏览器
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-liulanqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-lianjieliu"></span>
|
||||
<div class="name">
|
||||
连接流
|
||||
</div>
|
||||
<div class="code-name">.icon-rtc-file-lianjieliu
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-rtc-file-sanjiaoxing"></span>
|
||||
<div class="name">
|
||||
@@ -1282,6 +1342,38 @@
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-daohang-shujufenxi"></use>
|
||||
</svg>
|
||||
<div class="name">数据汇总</div>
|
||||
<div class="code-name">#icon-rtc-file-daohang-shujufenxi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-kongzhuangzhongduan"></use>
|
||||
</svg>
|
||||
<div class="name">控桩终端</div>
|
||||
<div class="code-name">#icon-rtc-file-kongzhuangzhongduan</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-liulanqi"></use>
|
||||
</svg>
|
||||
<div class="name">浏览器</div>
|
||||
<div class="code-name">#icon-rtc-file-liulanqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-lianjieliu"></use>
|
||||
</svg>
|
||||
<div class="name">连接流</div>
|
||||
<div class="code-name">#icon-rtc-file-lianjieliu</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-rtc-file-sanjiaoxing"></use>
|
||||
|
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4147343 */
|
||||
src: url('iconfont.woff2?t=1688291642630') format('woff2'),
|
||||
url('iconfont.woff?t=1688291642630') format('woff'),
|
||||
url('iconfont.ttf?t=1688291642630') format('truetype');
|
||||
src: url('iconfont.woff2?t=1688799262496') format('woff2'),
|
||||
url('iconfont.woff?t=1688799262496') format('woff'),
|
||||
url('iconfont.ttf?t=1688799262496') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
@@ -13,6 +13,22 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-rtc-file-daohang-shujufenxi:before {
|
||||
content: "\e61d";
|
||||
}
|
||||
|
||||
.icon-rtc-file-kongzhuangzhongduan:before {
|
||||
content: "\e6bb";
|
||||
}
|
||||
|
||||
.icon-rtc-file-liulanqi:before {
|
||||
content: "\e6e8";
|
||||
}
|
||||
|
||||
.icon-rtc-file-lianjieliu:before {
|
||||
content: "\ec57";
|
||||
}
|
||||
|
||||
.icon-rtc-file-sanjiaoxing:before {
|
||||
content: "\e619";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@@ -5,6 +5,34 @@
|
||||
"css_prefix_text": "icon-rtc-file-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "3494344",
|
||||
"name": "数据汇总",
|
||||
"font_class": "daohang-shujufenxi",
|
||||
"unicode": "e61d",
|
||||
"unicode_decimal": 58909
|
||||
},
|
||||
{
|
||||
"icon_id": "25071396",
|
||||
"name": "控桩终端",
|
||||
"font_class": "kongzhuangzhongduan",
|
||||
"unicode": "e6bb",
|
||||
"unicode_decimal": 59067
|
||||
},
|
||||
{
|
||||
"icon_id": "25853325",
|
||||
"name": "浏览器",
|
||||
"font_class": "liulanqi",
|
||||
"unicode": "e6e8",
|
||||
"unicode_decimal": 59112
|
||||
},
|
||||
{
|
||||
"icon_id": "5961312",
|
||||
"name": "连接流",
|
||||
"font_class": "lianjieliu",
|
||||
"unicode": "ec57",
|
||||
"unicode_decimal": 60503
|
||||
},
|
||||
{
|
||||
"icon_id": "3101162",
|
||||
"name": "三角形",
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user