-
Notifications
You must be signed in to change notification settings - Fork 0
amswf/taskshare
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
<template> <div class="demo" ref="demo"> <!-- 注意 这里我设置了静音 muted --> <video ref="video" autoplay muted playsinline webkit-playsinline style="width:0;height:0" ></video> </div> </template> <script> // import Vconsole from 'vconsole' import * as THREE from 'three' import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js' import * as d3 from 'd3' // webrtc 资料 // https://blog.csdn.net/yangzhenping/article/details/78895208?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164817406916782246448540%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164817406916782246448540&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-78895208.142^v3^pc_search_result_control_group,143^v4^register&utm_term=getUserMedia+%E7%94%BB%E8%B4%A8%E4%BD%8E export default { name: 'demo', data() { return { scene: null, camera: null, renderer: null, moduleObj: null, canvasblobs: [], mediaRecorder: null, canvasStream: null, // 双指 // 第一个手指数据 oneData: [], // 第二个手指数据 twoData: [], long: 0 } }, async mounted() { // return // 从这里保存这里的this 为了接下里的函数使用 const self = this const box = this.$refs.demo box.style.height = window.innerHeight + 'px' // 场景 const scene = new THREE.Scene() // 摄像机 const camera = new THREE.PerspectiveCamera( 30, box.clientWidth / box.clientHeight, //使摄像机和屏幕比一样 1, 1000 ) // 渲染器 这个配置 是为了开启一个什么缓冲区 不开这个 无法拍照 const renderer = new THREE.WebGLRenderer({ preserveDrawingBuffer: true }) // 设置渲染器大小 renderer.setSize(box.clientWidth, box.clientHeight) /* 包含颜色信息(.map、.emissiveMap 和 .specularMap)的纹理在 glTF 中始终使用 sRGB 颜色空间, 而顶点颜色和材质属性(.color、.emissive、.specular)使用线性颜色空间。在典型的渲染工作流程中, 渲染器将纹理转换为线性色彩空间,进行光照计算,然后将最终输出转换回 sRGB 并显示在屏幕上。 除非您需要在线性色彩空间中进行后期处理,否则在使用 glTF 时始终按如下方式配置 WebGLRenderer : renderer.outputEncoding = THREE.sRGBEncoding; */ // renderer.setClearColor('#fff', 1.0) // 向赫兹中加入渲染器 box.appendChild(renderer.domElement) renderer.outputEncoding = THREE.sRGBEncoding // 摄像头录制 这个函数有很多版本 用来兼容各个浏览器的,当然当前这个就是兼容性最好的,可以去网上搜一下用来专门兼容其他浏览器的,然后进行判断和选择 // console.log(navigator.mediaDevices.getUserMedia) // navigator.getUserMedia = // navigator.mediaDevices.getUserMedia || // navigator.webkitGetUserMedia || // navigator.mozGetUserMedia // const mediaStream = await navigator.mediaDevices.getUserMedia({ // // video属性设置 开摄像头的 // video: { // width: { min: box.clientWidth }, // height: { min: box.clientHeight } // // 下面这个是开后置摄像头的 根据需要打开 // // facingMode: { exact: "environment" }, // }, // // audio属性设置 开麦克风的 // audio: { // // 是否尝试去除音频信号中的背景噪声 // noiseSuppression: true, // // 回声取消 // echoCancellation: true // } // }) /* 权限 在一个可安装的app(如Firefox OS app)中使用 getUserMedia() ,你需要在声明文件中指定以下的权限: "permissions": { "audio-capture": { "description": "Required to capture audio using getUserMedia()" }, "video-capture": { "description": "Required to capture video using getUserMedia()" } } */ // const video = this.$refs.video // // 成功返回promise对象,接收一个mediaStream参数与video标签进行对接 // video.srcObject = mediaStream // video.play() // 加入作为背景的面 这里干的事情,就是用一个平面模型 把模型的材质变成了录制的视频 // const texture = new THREE.VideoTexture(video) // texture.minFilter = THREE.LinearFilter // texture.magFilter = THREE.LinearFilter // texture.format = THREE.RGBFormat const bgw = 30 //页面 图像出现压扁 拉长情况 是在这个地方去配置的,第一个参数是宽度的截取 第二个参数是高度的截取 // 苹果手机出现的拉长 在这里加个if判断 是苹果的话,算法就改变一下 把第一个参数 变得更多一些 就正常了 const geometry = new THREE.PlaneGeometry( bgw, bgw / (box.clientWidth / box.clientHeight) ) // const material = new THREE.MeshBasicMaterial({ // map: texture, // side: THREE.DoubleSide // }) // const cube = new THREE.Mesh(geometry, material) camera.position.z = 100 //参数 // 以下这行代码 代表着是否横向翻转镜像 打开,就像照镜子一样了 // cube.rotation.y += 3.1415 //增加环境光 var light = new THREE.AmbientLight( 0xffffff,0.3); scene.add( light ); var directionalLight = new THREE.DirectionalLight( 0xffffff, 2.5 ); scene.add( directionalLight ); // scene.add(cube) // 加载3D this.fnLoad3D() // cube.rotation.y = 1.6 function animate() { // 60赫兹刷新率的执行本函数, 比定时器更流畅更适合屏幕 requestAnimationFrame(animate) // 如何引入了模型 就把模型里的内容 进行改变 // 以下这两行 打开后 可以去看整体是咋样的 搞不明白物体位置时候 就尝试打开 if(self.moduleObj){ self.moduleObj.rotation.y += 0.01 self.moduleObj.rotation.x += 0.01 } // /渲染 renderer.render(scene, camera) } animate() this.scene = scene this.camera = camera this.renderer = renderer // this.initStream = mediaStream // // 初始位置 // self.moduleObj.scene.rotation.y = 0 // self.moduleObj.scene.rotation.x = 0 // 缩放 去设置双指时候执行 333行 this.fnDo() }, methods: { // 加载3D模型的函数 fnLoad3D() { // 注意这个loader 是专门引入.glb格式的模型的 需要的话 请在three官网找需要的loader 而引入地址是一样的 const loader = new GLTFLoader() // 这块就是引入模型地址的方式 可以使用动态引入 loader.load( 'http://qiyuan.mobi/3d/20220330/lingpai/models/lingpai.gltf', model => { // 把模型的东西 放入这个盒子里 this.scene.add(model.scene) this.moduleObj = model this.moduleObj.scene.rotation.y = 180 // !注意, 因为我不知道导入的模型都会有啥,我也没完整学过模型的东西 所以 这里导入复杂的后 怎么处理模型 均需要您在three里面找到对应的方法 // 可以通过if(...) 这样来判断某些东西存在 就引入 不存在就不引入的方式进行 关于引入模型包含的那些字段 您学模型的肯定知道都啥意思 // 还有就是 我的模型 导出时候 选择了 !! 自发光 !! 如果您那边出现了看不见模型 也没有报错信息 就可能是灯光方面的问题 也或者加个全局日光灯 或者在摄像机方向对着加个光 // http://www.yanhuangxueyuan.com/threejs/docs/#api/zh/lights/AmbientLight 全局环境光 // 当然 也可能是外包的制作模型的那些人导出没有带上所有东西的问题 // s.prototype.$vConsole =new Vconsole() }, undefined, function(error) { console.log(error) } ) }, // 为了改变每个物体按照自己的原点旋转 changePivot(obj, scene) { let center = new THREE.Vector3() obj.geometry.computeBoundingBox() obj.geometry.boundingBox.getCenter(center) let wrapper = new THREE.Object3D() wrapper.position.set(center.x, center.y, center.z) obj.position.set(-center.x, -center.y, -center.z) // 注意这个鬼东西,会删了原数组中的数据,导致循环不再正确进行 wrapper.add(obj) scene.add(wrapper) return wrapper }, // 开始录制canvas async fnPlayCanvas() { let mimeType = '' // 这里判断各种格式 浏览器是否支持 不同格式清晰度也不一样 // 关于各个类型 清晰度和支持情况 https://developer.mozilla.org/en-US/docs/Web/Media/Formats/WebRTC_codecs var types = [ 'video/webm', 'video/mp4', 'video/avc', 'video/ogg', 'video/webm\;codecs=vp8', 'video/webm\;codecs=daala', 'video/webm\;codecs=h264', 'audio/webm\;codecs=opus', 'video/mpeg' ] for (var i in types) { if (MediaRecorder.isTypeSupported(types[i])) { mimeType = types[i] break } } if (mimeType === '') { alert('当前浏览器版本过低,请下载新版浏览器') } //防止重复点击 if (this.canvasblobs.length > 0) { this.canvasblobs = [] } this.$refs.video.play() // 录制屏幕 const canvas = document.querySelector('.demo canvas') const Stream = canvas.captureStream() // 保存这个canvas流 用于截取拍照 this.canvasStream = Stream // 合并音轨与视频轨道 const newStream = new MediaStream([ // 获取视频轨道 Stream.getVideoTracks()[0], // 获取音轨 注意这个音轨,我并未开启音乐播放器 如果需要添加进去多个音轨,或者其他音轨 那后面的0可能就需要改掉, // 如果想加入所有音轨 ...this.initStream.getAudioTracks() 用这个方法 三点运算符 this.initStream.getAudioTracks()[0] ]) let mediaRecorder = new MediaRecorder(newStream, { // 这个地方的webm也能设置不同的清晰度 mimeType, ignoreMutedMedia: true, // // 音质 audioBitsPerSecond: 128000, // 画质 可以调高 videoBitsPerSecond: 2500000 }) mediaRecorder.ondataavailable = e => { // 保存canvas流数据 this.canvasblobs.push(e.data) } mediaRecorder.start(60) //60毫秒记录一次 this.mediaRecorder = mediaRecorder }, // 保存canvas数据 fnDownloadCanvas() { this.mediaRecorder.stop() // webm是一种浏览器比较通用的视频格式 // https://www.npmjs.com/package/webm-to-mp4 这个是一个webm转MP4的js库 可以让后端整个连接 用nodejs加这个库来转换一下 // 如果是其他的语言 直接搜索 webm转MP4 就能找到 这个是java的 https://www.cnblogs.com/zhwl/p/3645593.html // 注意 !!!!!!!!! 苹果的safari就支持MP4 const ua = navigator.userAgent // console.log(navigator.userAgent) const isSafari = /(?:Safari)/.test(ua) const isAndroid = /(?:Android)/.test(ua) let strEnd = 'webm' if (isSafari && !isAndroid) { strEnd = 'mp4' } let blob = new Blob(this.canvasblobs, { type: `video/${strEnd}` }) let url = URL.createObjectURL(blob) // console.log(url) let a = document.createElement('a') a.href = url a.style.display = 'none' a.download = `record.${strEnd}` a.click() }, // 拍照 fnTakeImg() { this.renderer.domElement.toBlob( function(blob) { let dlLink = document.createElement('a') dlLink.download = 'newImg' dlLink.style.display = 'none' // 字符内容转变成blob地址 dlLink.href = URL.createObjectURL(blob) // 触发点击 dlLink.click() }, 'image/png', 1 ) }, // 移动模式 fnMove() { const self = this d3.select('.demo canvas').call( d3.drag().on('drag', function() { // 这里其实就是每隔一段时间计算手指位移量 const d = d3.event const cx = d.dx const cy = d.dy // 上面把每一段时间的位移量 进行一个缩小 改变模型内部的position属性就可以位移了 self.moduleObj.scene.position.y -= cy / 14 self.moduleObj.scene.position.x += cx / 14 // 这个z轴的 就是离摄像机的距离 也就实现了深度 // self.moduleObj.scene.position.z }) ) }, // 旋转模式 fnRotate() { const self = this d3.select('.demo').call( d3.drag().on('drag', function() { // 这里其实就是每隔一段时间计算手指位移量 const d = d3.event if (d.active !== 1) { const cx = d.dx const cy = d.dy self.moduleObj.scene.rotation.y -= cx / 14 self.moduleObj.scene.rotation.x += cy / 14 } // 上面把每一段时间的位移量 进行一个缩小 改变模型内部的rotation属性就可以位移了 // 这里使用时候 注意我跟上面的位移相比 cx 与 cy是反着用的 因为这样更符合操作习惯 // 这个z轴的 但是对于渲染 两个轴向也够了 // self.moduleObj.scene.rotation.z }) ) }, // 缩放 fnDo() { const self = this d3.select('.demo canvas').call( d3.drag().on('drag', function() { const d = d3.event // 旋转 if (d.active == 1) { const cx = d.dx const cy = d.dy self.moduleObj.scene.rotation.y -= cx / 14 self.moduleObj.scene.rotation.x += cy / 14 } // 缩放 if (d.active == 2) { // 两个手指移动了的距离 if (d.identifier === 0) { // 初始数据不计算 if (self.oneData.length === 0) { self.oneData = [d.x, d.y] self.long = Math.sqrt( (self.oneData[0] - self.twoData[0]) ** 2 + (self.oneData[1] - self.twoData[1]) ** 2 ) return } self.oneData = [d.x, d.y] } if (d.identifier === 1) { if (self.twoData.length === 0) { self.twoData = [d.x, d.y] self.long = Math.sqrt( (self.oneData[0] - self.twoData[0]) ** 2 + (self.oneData[1] - self.twoData[1]) ** 2 ) return } self.twoData = [d.x, d.y] } const long = Math.sqrt( (self.oneData[0] - self.twoData[0]) ** 2 + (self.oneData[1] - self.twoData[1]) ** 2 ) let num = long - self.long self.long = long try { self.moduleObj.scene.scale.y += num/20 //调整这里数字来调整缩小放大的速度 self.moduleObj.scene.scale.x += num/20 self.moduleObj.scene.scale.z += num/20 } catch (error) { // alert(error) } } }) ) } } } </script> <style> * { padding: 0; margin: 0; } </style> <style scoped> .demo { position: relative; display: flex; flex-direction: column; width: 100vw; overflow: hidden; } .btns { width: 15vw; height: 15vw; font-size: 16px; border-radius: 50%; border: 0; background: #ffffff00; } .btns:active { background: #ffffff00; } .btnsBox { position: fixed; width: 100vw; bottom: 0; left: 0; } </style>
About
base on flash p2p,air.use for share task.
Resources
Stars
Watchers
Forks
Releases
No releases published
Packages 0
No packages published