赞
踩
webGL(web图形库)是一个中JavaScript API,用于在web浏览器中呈现交互式2D与3D图形,而且无需使用插件。
ThreeJs 是一款WebGL框架(在其API接口基础上又进行了一层封装),
Three.js是基于原生WebGL封装运行的三维引擎
git地址
下载dev 压缩包
工程化开发,在vue里面npm下载three.js
import 引入
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../three.js-dev/build/three.js"></script> <script src="../three.js-dev/examples/js/controls/OrbitControls.js"></script> </head> <body> <script> //虚拟场景+相机+渲染器=效果 // //创建一个三维场景 const scene = new THREE.Scene() // //创建一个几何体长宽高 const geometry = new THREE.BoxGeometry(100, 100, 100) // //设置材质和颜色 const material = new THREE.MeshLambertMaterial({ transparent: true, //开启透明 opacity: 0.5, //设置透明度 color: 0xc708fd }) //设置完会全黑,需要设置光源 // //创个一个网格模型对象 const mesh = new THREE.Mesh(geometry, material) // //给网格模型添加到三维场景 scene.add(mesh); // mesh.position.set(100, 100, 100) //设置在坐标轴的位置 // mesh.rotateX(Math.PI / 4) //旋转 //设置环境光源 const Ambient = new THREE.AmbientLight(0xffffff, 0.5); //环境光 scene.add(Ambient); //设置点光源 const Point = new THREE.PointLight(0xffffff, 1); Point.position.set(100, 100, 100); //200,200,200是在三个角的夹角 scene.add(Point); //设置可视化点光源 一个白色三角 光照过来的位置 const pointLightHelper = new THREE.PointLightHelper(Point, 10) scene.add(pointLightHelper) //设置坐标轴 const axesHelper = new THREE.AxesHelper(150); scene.add(axesHelper); // //怎么呈现到web网页上:通过相机和渲染器把三维场景scene渲染出来 // //1.创建一个透视相机 const width = 800 const height = 500 //45:视场角度,width/height:canvas画布宽高比,1:近裁截面,3000,远裁截面 const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000) //透视投影 camera.position.set(200, 200, 200) //相机所在位置 camera.lookAt(0, 0, 0) //相机所照方向 (0,0,0原点)位置+坐标会构成一条线决定你的观察方向 // //创建webGl渲染器 const renderer = new THREE.WebGLRenderer() renderer.setSize(width, height) //设置渲染区域尺寸 renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色 renderer.render(scene, camera) //渲染操作 document.body.appendChild(renderer.domElement) //body元素中插入canvas对象,相当于canvas的画布 //使用OrbitControls轨道控制器需要引入(可以360°转动) const controls = new THREE.OrbitControls(camera, renderer.domElement) // //使用OrbitControls轨道控制器需,重新进行更新 // controls.addEventListener('change', function () { // renderer.render(scene, camera) //重新渲染 // }) //动画使用 requestAnimationFrame()实现动画效果,有动画渲染就不需要controls.addEventListener('change', function () {}) //动画的原理就是一张一张拼起来的照片,只要运动够快就是动画 //1.通过照相每次改变都需要从新呈现 const clock = new THREE.Clock() //获取上一次和这次渲染时间间隔 function render() { const spt = clock.getDelta * 1000 //渲染时间间隔毫秒 const zl = 1000 / spt //渲染帧率 一秒渲染多少次 比如一秒渲染60次 renderer.render(scene, camera) mesh.rotateY(0.01) requestAnimationFrame(render) } render() </script> </body> </html>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <script src="../three.js-dev/build/three.js"></script> <script src="../three.js-dev/examples/js/controls/OrbitControls.js"></script> </head> <body> <script> //虚拟场景+相机+渲染器=效果 // //创建一个三维场景 const scene = new THREE.Scene() // //创建一个几何体长宽高 const geometry = new THREE.BoxGeometry(100, 100, 100) // //设置材质和颜色 const material = new THREE.MeshLambertMaterial({ transparent: true, //开启透明 opacity: 0.5, //设置透明度 color: 0xc708fd }) //设置完会全黑,需要设置光源 // //创个一个网格模型对象 // const mesh = new THREE.Mesh(geometry, material) // // //给网格模型添加到三维场景 // scene.add(mesh); for (let i = 0; i < 10; i++) { //批量创建很多个立方体 for (let j = 0; j < 10; j++) { const mesh = new THREE.Mesh(geometry, material) mesh.position.set(i * 200, 0, j * 200, ) scene.add(mesh); } } // mesh.position.set(100, 100, 100) //设置在坐标轴的位置 // mesh.rotateX(Math.PI / 4) //旋转 //设置环境光源 const Ambient = new THREE.AmbientLight(0xffffff, 0.5); //环境光 scene.add(Ambient); //设置点光源 const Point = new THREE.PointLight(0xffffff, 1); Point.position.set(100, 100, 100); //200,200,200是在三个角的夹角 scene.add(Point); //设置可视化点光源 一个白色三角 光照过来的位置 // const pointLightHelper = new THREE.PointLightHelper(Point, 10) // scene.add(pointLightHelper) //设置坐标轴 const axesHelper = new THREE.AxesHelper(150); scene.add(axesHelper); // //怎么呈现到web网页上:通过相机和渲染器把三维场景scene渲染出来 // //1.创建一个透视相机 const width = 800 const height = 500 //45:视场角度,width/height:canvas画布宽高比,1:近裁截面,3000,远裁截面 const camera = new THREE.PerspectiveCamera(45, width / height, 1, 3000) //透视投影 camera.position.set(800, 800, 800) //相机所在位置 camera.lookAt(1000, 0, 1000) //相机所照方向 (0,0,0原点)位置+坐标会构成一条线决定你的观察方向 // lookAt会影响OrbitControls轨道控制器的区域 // //创建webGl渲染器 const renderer = new THREE.WebGLRenderer() renderer.setSize(width, height) //设置渲染区域尺寸 renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色 renderer.render(scene, camera) //渲染操作 document.body.appendChild(renderer.domElement) //body元素中插入canvas对象,相当于canvas的画布 //使用OrbitControls轨道控制器需要引入(可以360°转动) const controls = new THREE.OrbitControls(camera, renderer.domElement) controls.target.set(1000, 0, 1000) controls.update() // //使用OrbitControls轨道控制器需,重新进行更新 // controls.addEventListener('change', function () { // renderer.render(scene, camera) //重新渲染 // }) //动画使用 requestAnimationFrame()实现动画效果,有动画渲染就不需要controls.addEventListener('change', function () {}) //动画的原理就是一张一张拼起来的照片,只要运动够快就是动画 //1.通过照相每次改变都需要从新呈现 const clock = new THREE.Clock() //获取上一次和这次渲染时间间隔 function render() { const spt = clock.getDelta * 1000 //渲染时间间隔毫秒 const zl = 1000 / spt //渲染帧率 一秒渲染多少次 比如一秒渲染60次 renderer.render(scene, camera) // mesh.rotateY(0.01) requestAnimationFrame(render) } render() </script> </body> </html>
//几大难点
1.太阳光源:方向光DirectionalLight
1.确认太阳位置:通过SunCalc.js js库 传入经纬度时间可以得出太阳的高度角,方位角,然后通过three的方法角度转弧度,转换成方向光的position
2.设置阴影的属性:密度,最远.最近距离,清晰度....
3.阴影水波纹的问题: this.directionalLight.shadow.bias = -0.01;(调整阴影偏差)
4.画模型加压缩:用SketchUp(草图大师),导出gltf模型,gltf-pipeline压缩,放到七牛云上
5.载入模型:gltf-pipeline压缩后使用DRACOLoader加载
6.网页端和移动端:mousedown,touchstart/touchmove/touchend
7.小程序镶嵌网页:日期格式,window获取不到问题
<template> <div id="sunshine" style="width: 100vw; height: 100vh; overflow: hidden"> <div class="compass"> <img src="../assets/images/compass.png" class="icon" style="transform: rotate(0deg)" /> </div> <div class="page-wrappr" id="app2"> <div class="bottom-card"> <div class="top"> <h4 class="title">{{ newhouseName }}</h4> <div class="date" style="float: right"> <span class="text" ><el-date-picker v-model="date" type="date" :editable="false" placeholder="选择日期" format="yyyy/MM/dd" value-format="yyyy/MM/dd" @change="changDate" @focus="focusDate" > </el-date-picker></span ><i class="van-icon van-icon-arrow"> </i> </div> </div> <div class="time-wrapper"> <div class="play-btn play-btn-bf" @click="bfBtnFun()"> <svg v-if="isPlay" t="1689756307920" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="19031" width="48" height="48" > <path d="M426.666667 138.666667v746.666666a53.393333 53.393333 0 0 1-53.333334 53.333334H266.666667a53.393333 53.393333 0 0 1-53.333334-53.333334V138.666667a53.393333 53.393333 0 0 1 53.333334-53.333334h106.666666a53.393333 53.393333 0 0 1 53.333334 53.333334z m330.666666-53.333334H650.666667a53.393333 53.393333 0 0 0-53.333334 53.333334v746.666666a53.393333 53.393333 0 0 0 53.333334 53.333334h106.666666a53.393333 53.393333 0 0 0 53.333334-53.333334V138.666667a53.393333 53.393333 0 0 0-53.333334-53.333334z" fill="#f01e03" p-id="19032" ></path> </svg> <svg v-else t="1689756265979" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="17742" width="48" height="48" > <path d="M870.2 466.333333l-618.666667-373.28a53.333333 53.333333 0 0 0-80.866666 45.666667v746.56a53.206667 53.206667 0 0 0 80.886666 45.666667l618.666667-373.28a53.333333 53.333333 0 0 0 0-91.333334z" fill="#f01e03" p-id="17743" ></path> </svg> </div> <div class="time-slider"> <span class="time">04:49</span> <div class="van-slider van-slider" style="height: 4px"> <div class="van-slider__bar" style="width: 0; height: 4px; background: rgb(255, 204, 204)" > <div role="slider" tabindex="0" class="van-slider__button-wrapper" @mousedown.prevent="SunIconMousestartPC" style="position: absolute; top: -15px; left: 0%" v-if="isPc" > <div class="custom-button"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAqCAYAAAAu9HJYAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKaADAAQAAAABAAAAKgAAAACHgP6PAAAEK0lEQVRYCe1YSU8UQRSuYhpZ4gJoNDHxYMAFuWhiNCMHNQwICIgHjHr06kFvbhcuarh54De4kbgOLoBGDkgkGr2YqNF400hUQAzrMOX3qnuk6KnuqZ7p8WQlL/36vXrf+7q6a3nNWYhNNDVVskTikYS0rEb+8OGnMOALwgD5i7GwcIQJUSWF9JBauCQZK1J4qbpiDq6GTTI4A4OIsEnOKjlVXTEHV8MlGYncZJx/lEJ6SI3rcERHx3I2NnYNyVbAf5r397/R9QvDJurrtwPnCibbJCsvP8Z7en67cfUjOTHRiI4tCNyL6xMsLTvcgWHcO7hPnDwtzM6bBq0nWVT0DKP4U/YWogJr34Bobt6cFp2DQeIBFwQrJAzlo7yaZmlsjN+//x1PGSNyEoSA5udPou8pd3/R2WmxoSEa8XZIFLIeshryA/IFMgy5w2prB3lnZwK63QhPJWhZMcqbcqtX7TeZ6iBfRyJxF/drMbJHeV/fnZSPrvieOnC5hGRVql2r04Ri7Dy+7x7yi4aGdsRdhzrKLOsQdqfXZNc1X5IUgEkUYVNTK3lv71gKQLS1rYDtKu5bUrYA1zgrLT3O792bFAcPlkP/hcmy4BefkaQ7GATXgeBT2Le5fcb3nL9lJSV1IPrNJEY/cTwiMarL2PT0LbizJ0jYQtQQjsTzyKWaA5Fk4+OXkWCPCpC1TjiEZ9CMXzc+9I0g+A6yzADXrAvnc5iQWzEhP/sFmI9kMnk2VILEih5YiDN+BMlnRBJrYQGemNbBfLTDEt8H2YgkGx7eiSde64OTvYtwCd+nWViwK5l9ik4dUmcZTjBLjv7JZKUPRu4uG3/Ei4slaxL3jiHECWTepGSnrS6fzcan+kjDxex155OeAbaFfbNR97pdsXRQyGez8T24GK2T4sCBXXiQF3ljGYns5o8fj3jhm73uaPQllqBRL5Cc7IRL+D7NiCTOgUlg3PbBycV128H3xDAiKaM578JoznkiZeOwt8WuTKHGJJ39tTsTYEB/d6Z9m/CMScrkZWXnMJrPAxLRdyccwjNoRrNbxZGH3ulpqvBqVHsgPexDL5UP8pjvsJCn6ZISKrjigYgtdo7jVB4lHMKV5cmiT6v5jmTGQiwWO4LXfzFtK9OlokJMiAt8YOAmuZ1C7AbUb5kKMc9v0iFIJe0GSBFkvzu3TFhbWw2iMUg35BXkK4QOs3Sle7LHUNJWpwhKHMKzz5MbZF3v8wNCO5KitXUNm5l5D5DFwr2wMMofPPjgJprtvfw5MD8/vCRHcfEWXe2tH8nZ2X1LgqlwD5EgPZjEAy5GefFPCeXVND3JVavol3IcAIO41vkV7hpMY5ODW+fkiTM7b1q89nWn9TI0yEPr/3/mhqOVoVuqBKFuqp4hzN+t/yb9Y/65N2yS6n9yVc/pwcIlmad/5n8AC/ylWqliwkYAAAAASUVORK5CYII=" alt="" class="sun-icon" /> </div> </div> <div role="slider" tabindex="0" class="van-slider__button-wrapper" @touchstart.prevent="SunIconMousestart" @touchmove.prevent="SunIconMousemove" @touchend.prevent="SunIconMouseend" style="position: absolute; top: -15px; left: 0%" v-else > <div class="custom-button"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACkAAAAqCAYAAAAu9HJYAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAKaADAAQAAAABAAAAKgAAAACHgP6PAAAEK0lEQVRYCe1YSU8UQRSuYhpZ4gJoNDHxYMAFuWhiNCMHNQwICIgHjHr06kFvbhcuarh54De4kbgOLoBGDkgkGr2YqNF400hUQAzrMOX3qnuk6KnuqZ7p8WQlL/36vXrf+7q6a3nNWYhNNDVVskTikYS0rEb+8OGnMOALwgD5i7GwcIQJUSWF9JBauCQZK1J4qbpiDq6GTTI4A4OIsEnOKjlVXTEHV8MlGYncZJx/lEJ6SI3rcERHx3I2NnYNyVbAf5r397/R9QvDJurrtwPnCibbJCsvP8Z7en67cfUjOTHRiI4tCNyL6xMsLTvcgWHcO7hPnDwtzM6bBq0nWVT0DKP4U/YWogJr34Bobt6cFp2DQeIBFwQrJAzlo7yaZmlsjN+//x1PGSNyEoSA5udPou8pd3/R2WmxoSEa8XZIFLIeshryA/IFMgy5w2prB3lnZwK63QhPJWhZMcqbcqtX7TeZ6iBfRyJxF/drMbJHeV/fnZSPrvieOnC5hGRVql2r04Ri7Dy+7x7yi4aGdsRdhzrKLOsQdqfXZNc1X5IUgEkUYVNTK3lv71gKQLS1rYDtKu5bUrYA1zgrLT3O792bFAcPlkP/hcmy4BefkaQ7GATXgeBT2Le5fcb3nL9lJSV1IPrNJEY/cTwiMarL2PT0LbizJ0jYQtQQjsTzyKWaA5Fk4+OXkWCPCpC1TjiEZ9CMXzc+9I0g+A6yzADXrAvnc5iQWzEhP/sFmI9kMnk2VILEih5YiDN+BMlnRBJrYQGemNbBfLTDEt8H2YgkGx7eiSde64OTvYtwCd+nWViwK5l9ik4dUmcZTjBLjv7JZKUPRu4uG3/Ei4slaxL3jiHECWTepGSnrS6fzcan+kjDxex155OeAbaFfbNR97pdsXRQyGez8T24GK2T4sCBXXiQF3ljGYns5o8fj3jhm73uaPQllqBRL5Cc7IRL+D7NiCTOgUlg3PbBycV128H3xDAiKaM578JoznkiZeOwt8WuTKHGJJ39tTsTYEB/d6Z9m/CMScrkZWXnMJrPAxLRdyccwjNoRrNbxZGH3ulpqvBqVHsgPexDL5UP8pjvsJCn6ZISKrjigYgtdo7jVB4lHMKV5cmiT6v5jmTGQiwWO4LXfzFtK9OlokJMiAt8YOAmuZ1C7AbUb5kKMc9v0iFIJe0GSBFkvzu3TFhbWw2iMUg35BXkK4QOs3Sle7LHUNJWpwhKHMKzz5MbZF3v8wNCO5KitXUNm5l5D5DFwr2wMMofPPjgJprtvfw5MD8/vCRHcfEWXe2tH8nZ2X1LgqlwD5EgPZjEAy5GefFPCeXVND3JVavol3IcAIO41vkV7hpMY5ODW+fkiTM7b1q89nWn9TI0yEPr/3/mhqOVoVuqBKFuqp4hzN+t/yb9Y/65N2yS6n9yVc/pwcIlmad/5n8AC/ylWqliwkYAAAAASUVORK5CYII=" alt="" class="sun-icon" /> </div> </div> </div> </div> <span class="time">19:40</span> </div> <span class="current-time">10:43</span> </div> <div class="time-labels"> <span class="rise-time">日出时间</span ><span class="set-time">日落时间</span><span class="value"> </span> </div> <div class="dates"> <button class="btn" @click="jq(0)">大寒</button ><button class="btn" @click="jq(1)">春分</button ><button class="btn" @click="jq(2)">夏至</button ><button class="btn" @click="jq(3)">秋分</button ><button class="btn" @click="jq(4)">立冬</button ><button class="btn" @click="jq(5)">冬至</button> </div> </div> </div> </div> </template> <script> import dayjs from "dayjs"; import * as THREE from "three"; import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";//控制器 import * as SunCalc from "../assets/js/suncalc.js";//JS库用来获取太阳位置,日出日落时间 import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";//glft模型 import { getSunshineNewhouse } from "@/api/house/newhouse"; import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js";//gltf-pipeline压缩后使用DRACOLoader加载 export default { name: "sunshine", metaInfo() { return { title: "房大地-日照分析", }; }, data() { return { imageUrl: "https://****.com/", renderer: null, latitude: 39.899746, //经纬度 longitude: 116.409337, date: null, camera: null, //相机 scene: null, //屏幕 ambientLight: null, //光照 directionalLight: null, //光照 SunIcon: null, //滑动块 vanSliderBar: null, //阴影进度条 vanSlider: null, //最长进度条 bfBtn: null, //播放按钮 ztBtn: null, //暂停按钮 HS: 0, //开始小时 MS: 0, //开始分钟 HL: 0, //结束小时 ML: 0, //结束分钟 timer: null, //计时器 sunrise: null, // 日出时间 sunset: null, // 日落时间 timeDifference: null, //时间差日落-日出 cube: null, plane: null, cube2: null, controls: null, //性能插件 isPlay: true, //播放按钮 DTURL: "", //底图url HouseURL: "", //3d模型url building: null, newhouseName: "", offsetDegree: 0, modelX: 0, //模型位置x modelY: 0, //模型位置y modelZ: 0, //模型位置z modelScale: "", //模型比例 planeScale: "", //底图比例 x1: 0, x2: 0, //滑块距离左边的位置 distance: 0, isPc: true, timerNum: 100, // intervalNum: 10, isDivMove: false, //小太阳是否拖拽 intersects: [], //几何体合计-点击事件 }; }, watch: {}, created() {}, mounted() { //判断是什么环境,手机还是电脑 if (document.documentElement.clientWidth < 720) { this.isPc = false; this.timerNum = 1; this.intervalNum = 100; } else { this.isPc = true; this.timerNum = 1; this.intervalNum = 10; } this.getSunshineFun(); }, methods: { //获取3d模型和底图数据 getSunshineFun() { getSunshineNewhouse(this.$route.params.id).then((res) => { if (res.data) { this.DTURL = this.imageUrl + res.data.backUrl; this.HouseURL = this.imageUrl + res.data.modelUrl; this.offsetDegree = res.data.offsetDegree; this.latitude = res.data.newhouseLat; this.longitude = res.data.newhouseLng; this.newhouseName = res.data.newhouseName; this.modelX = res.data.modelX; this.modelY = res.data.modelY; this.modelZ = res.data.modelZ; this.modelScale = res.data.modelScale; this.planeScale = res.data.planeScale; //获取今天日期 var D = new Date(); this.date = new Date(D.getFullYear(), D.getMonth(), D.getDate()); this.SunIcon = document.querySelector(".van-slider__button-wrapper"); this.vanSliderBar = document.querySelector(".van-slider__bar"); this.vanSlider = document.querySelector(".van-slider"); this.bfBtn = document.querySelector(".play-btn-bf"); //播放按钮 this.ztBtn = document.querySelector(".play-btn-zt"); //暂停按钮 this.initObj(); this.initRender(); this.initScene(); this.initCamera(); this.initLight(); this.initModel(); this.initControls(); this.animate(); this.SunCalcGetTimes(); window.addEventListener("resize", this.onWindowResize); } }); }, //初始相机 initCamera() { this.camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 3000 ); this.camera.position.set(0, 300, 250); //相机所在的位置,默认为(0,0,0)个单位; this.camera.lookAt(new THREE.Vector3(0, 0, 0)); //点击事件 }, //初始虚拟场景 initScene() { this.scene = new THREE.Scene(); this.scene.background = new THREE.Color("#b2e0ff"); this.scene.add(new THREE.AmbientLight(0xffffff, 1)); // 环境光 this.scene.receiveShadow = true; }, //初始three 虚拟场景+相机+渲染器=场景 initRender() { this.renderer = new THREE.WebGLRenderer({ antialias: true, logarithmicDepthBuffer: true, alpha: true, }); this.renderer.setSize(window.innerWidth, window.innerHeight); //告诉渲染器需要阴影效果 this.renderer.shadowMap.enabled = true; this.renderer.outputEncoding = THREE.SRGBColorSpace; //RGB模式编码(sRGBEncoding)进行对材质进行渲染,SRGBColorSpace this.renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap this.renderer.toneMapping = THREE.ReinhardToneMapping; this.renderer.toneMappingExposure = 1.1; // 默认1为了让场景更明亮 document.querySelector("#sunshine").appendChild(this.renderer.domElement); }, // 加载楼盘模型 initObj() { let _this = this; var loader = new GLTFLoader(); //gltf-pipeline压缩后使用DRACOLoader加载, gltf-pipeline -i 111.gltf -o modelDraco.gltf -d const dracoLoader = new DRACOLoader(); // 设置Draco解码库 该路径就是解压文件所在的位置,https://www.gstatic.com/draco/versioned/decoders/1.5.6/或者把node_modules/three/examples/jsm/libs/draco文件复制到public文件下 dracoLoader.setDecoderPath("/draco/"); // 使用js方式解压 dracoLoader.setDecoderConfig({ type: "js" }); // 初始化_initDecoder 解码器 dracoLoader.preload(); // 设置gltf加载器dracoLoader解码器 loader.setDRACOLoader(dracoLoader); loader.load( this.HouseURL, (gltf) => { const model = gltf.scene; gltf.scene.name = "模型1"; // 遍历模型中的所有子对象,设置阴影接收和投射属性 model.traverse(function (child) { if (child.isMesh) { // 重新设置材质 let scaleNum = _this.modelScale ? _this.modelScale : 2; child.scale.set(scaleNum, scaleNum, scaleNum); // 设置模型大小缩放 model.position.x = _this.modelX != 0 ? _this.modelX : 300; model.position.y = _this.modelY != 0 ? _this.modelY : 0; model.position.z = _this.modelZ != 0 ? _this.modelZ : -300; child.material.side = THREE.DoubleSide; //物体遮挡阴影 child.castShadow = true; child.receiveShadow = true; } }); _this.building = gltf.scene; _this.scene.add(gltf.scene); }, undefined, function (error) { console.error(error); } ); }, //底部草地+底图 initModel() { //底图 var planeGeometry = new THREE.CircleGeometry(110, 64); var texture = new THREE.TextureLoader().load(this.DTURL); texture.encoding = THREE.SRGBColorSpace; var planeMaterial = new THREE.MeshStandardMaterial({ map: texture, }); this.plane = new THREE.Mesh(planeGeometry, planeMaterial); this.plane.rotation.x = -0.5 * Math.PI; this.plane.position.y = 0; //物体遮挡阴影 this.plane.castShadow = true; //告诉底部平面需要接收阴影 this.plane.receiveShadow = true; this.scene.add(this.plane); //草地 var planeGeometry1 = new THREE.CircleGeometry(150, 64); var texture1 = new THREE.TextureLoader().load("/bj.png"); var planeMaterial1 = new THREE.MeshStandardMaterial({ map: texture1, }); this.plane1 = new THREE.Mesh(planeGeometry1, planeMaterial1); this.plane1.rotation.x = -0.5 * Math.PI; this.plane1.position.y = -1; //物体遮挡阴影 this.plane1.castShadow = true; //告诉底部平面需要接收阴影 this.plane1.receiveShadow = true; this.scene.add(this.plane1); }, //日出日落时间 SunCalcGetTimes() { if (typeof this.date == "string") { this.date = new Date(this.date); } else { this.date = new Date( this.date.getFullYear(), this.date.getMonth(), this.date.getDate() ); // 使用当前日期和时间 } var times = SunCalc.getTimes(this.date, this.latitude, this.longitude); let sunriseD = dayjs(times.sunrise); let sunriseF = sunriseD.format("HH:mm"); // 日出 this.sunrise = sunriseF; // 日落 this.sunset = dayjs(times.sunset).format("HH:mm"); document.querySelectorAll(".time")[0].innerHTML = this.sunrise; document.querySelectorAll(".time")[1].innerHTML = this.sunset; this.timeDifference = this.calculateTimeDifference( this.sunrise, this.sunset ); document.querySelector(".time-labels .value").innerHTML = "昼长:" + this.timeDifference.hours + "小时" + this.timeDifference.minutes + "分钟"; this.HS = this.sunrise.slice(0, 2) * 1; this.MS = this.sunrise.slice(3, 5) * 1; this.HL = this.sunset.slice(0, 2) * 1; this.ML = this.sunset.slice(3, 5) * 1; this.interval(); }, //计算太阳位置 SunCalcGetPosition(date) { var sunPosition = SunCalc.getPosition( date, this.latitude, this.longitude ); // 太阳在Three.js坐标系中的位置向量 const sunDirection = new THREE.Vector3(); if (this.offsetDegree > 0) { //角度转弧度 var offSetRad = THREE.MathUtils.degToRad(offsetDegree); sunDirection.setFromSphericalCoords( 1, Math.PI / 2 - sunPosition.altitude, -sunPosition.azimuth - offSetRad ); } else { sunDirection.setFromSphericalCoords( 1, Math.PI / 2 - sunPosition.altitude, -sunPosition.azimuth ); } sunDirection.normalize(); var sunlightPosition = sunDirection.clone().multiplyScalar(200); //光源到原点的距离 //设置太阳位置 this.directionalLight.position.copy(sunlightPosition); this.directionalLight.target.position.set(0, 0, 0); }, //太阳光阴影 initLight(a, b) { this.directionalLight = new THREE.DirectionalLight(0xffffff); this.directionalLight.visible = true; this.directionalLight.intensity = 3; //光线的密度,默认为1。 光照越强,物体表面就更明亮 this.directionalLight.shadow.camera.near = -300; //产生阴影的最近距离 this.directionalLight.shadow.camera.far = 300; //产生阴影的最远距离 this.directionalLight.shadow.camera.left = -300; //产生阴影距离位置的最左边位置 this.directionalLight.shadow.camera.right = 300; //最右边 this.directionalLight.shadow.camera.top = 300; //最上边 this.directionalLight.shadow.camera.bottom = -300; //最下面 this.directionalLight.shadow.bias = -0.01; //用于解决阴影水波纹条纹阴影的问题 this.directionalLight.shadow.mapSize.set(2048, 2048); //阴影清晰度 //告诉平行光需要开启阴影投射,物体遮挡阴影 this.directionalLight.castShadow = true; this.scene.add(this.directionalLight); }, //按住滑块拖动PC端 SunIconMousestart(e) { if (this.timer) { clearInterval(this.timer); this.timer = null; } // 获取鼠标在当前事件源的位置 this.x1 = e.touches[0].clientX; this.x2 = this.SunIcon.style.left.split("px")[0] * 1; }, // 小太阳滑块PC端 SunIconMousemove(e) { this.distance = e.touches[0].clientX - this.x1; if (this.SunIcon.style.left.split("px")[0] * 1 <= 0) { this.SunIcon.style.left = 0; this.vanSliderBar.style.width = 0 + "px"; this.HS = this.sunrise.slice(0, 2) * 1; this.MS = this.sunrise.slice(3, 5) * 1; } else if ( this.SunIcon.style.left.split("px")[0] * 1 >= this.vanSlider.clientWidth - this.SunIcon.clientWidth ) { this.SunIcon.style.left = this.vanSlider.clientWidth - this.SunIcon.clientWidth + "px"; this.vanSliderBar.style.width = this.vanSlider.clientWidth - this.SunIcon.clientWidth + "px"; this.HS = this.sunset.slice(0, 2) * 1; this.MS = this.sunset.slice(3, 5) * 1; } else { this.SunIcon.style.left = this.x2 + this.distance + "px"; this.vanSliderBar.style.width = this.x2 + this.distance + this.SunIcon.clientWidth + "px"; } //百分比 var bfb = ( (this.SunIcon.style.left.split("px")[0] * 1) / (this.vanSlider.clientWidth - this.SunIcon.clientWidth) ).toFixed(2); //进度条右侧时间 //最后时间 var l = this.sunset.slice(0, 2) * 60 + this.sunset.slice(3, 5) * 1; //开始时间 var s = this.sunrise.slice(0, 2) * 60 + this.sunrise.slice(3, 5) * 1; var a = (l - s) * bfb; var h = Math.floor((a + s) / 60); var m = ((s + a) % 60).toFixed(0) * 1; this.HS = h; this.MS = m; document.querySelector(".current-time").innerHTML = (h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m); }, // 松开小太阳移动端 SunIconMouseend(e) { this.isPlay = true; this.interval(); }, //按住太阳拖拽移动端 SunIconMousestartPC(e) { this.isDivMove = true; if (this.timer) { clearInterval(this.timer); this.timer = null; } // 获取鼠标在当前事件源的位置 var x1 = e.clientX - this.vanSlider.offsetLeft - this.SunIcon.offsetLeft; var _this = this; document.onmousemove = function (e) { var x2 = e.clientX; let distance = x2 - _this.vanSlider.offsetLeft - x1; console.log(distance); if (distance <= 0) { distance = 0; _this.HS = _this.sunrise.slice(0, 2) * 1; _this.MS = _this.sunrise.slice(3, 5) * 1; } else if ( distance >= _this.vanSlider.clientWidth - _this.SunIcon.clientWidth ) { distance = _this.vanSlider.clientWidth - _this.SunIcon.clientWidth; _this.HS = _this.sunset.slice(0, 2) * 1; _this.MS = _this.sunset.slice(3, 5) * 1; } else { _this.SunIcon.style.left = distance + "px"; _this.vanSliderBar.style.width = distance + _this.SunIcon.clientWidth + "px"; } //百分比 var bfb = ( distance / (_this.vanSlider.clientWidth - _this.SunIcon.clientWidth) ).toFixed(2); //进度条右侧时间 //最后时间 var l = _this.sunset.slice(0, 2) * 60 + _this.sunset.slice(3, 5) * 1; //开始时间 var s = _this.sunrise.slice(0, 2) * 60 + _this.sunrise.slice(3, 5) * 1; var a = (l - s) * bfb; var h = Math.floor((a + s) / 60); var m = ((s + a) % 60).toFixed(0) * 1; _this.HS = h; _this.MS = m; document.querySelector(".current-time").innerHTML = (h < 10 ? "0" + h : h) + ":" + (m < 10 ? "0" + m : m); }; document.onmouseup = function (e) { _this.isDivMove = false; _this.isPlay = true; _this.interval(); document.onmousemove = null; document.onmouseup = null; }; }, //初始化性能插件 initControls() { this.controls = new OrbitControls(this.camera, this.renderer.domElement); // 使动画循环使用时阻尼或自转 意思是否有惯性 this.controls.enableDamping = true; //是否可以缩放 this.controls.enableZoom = true; //是否自动旋转 this.controls.autoRotate = false; //设置相机距离原点的最远距离 this.controls.minDistance = 200; //设置相机距离原点的最远距离 this.controls.maxDistance = 600; //设置相机上下旋转最大角度最大到平面 this.controls.minPolarAngle = 0; this.controls.maxPolarAngle = Math.PI / 2 - 0.1; this.controls.addEventListener("change", (e) => { // 获取对象的旋转角度; const rotation = this.camera.rotation; // 判断中心轴旋转方向 // 定义变量来跟踪旋转角度 let rotationDegrees = { x: THREE.MathUtils.radToDeg(rotation.x), y: THREE.MathUtils.radToDeg(rotation.y), z: THREE.MathUtils.radToDeg(rotation.z), }; //左上角指南针转动 document.querySelector(".compass img").style.transform = "rotate(" + rotationDegrees.z + "deg)"; }); }, render() { this.renderer.render(this.scene, this.camera); }, //窗口变动触发的函数 onWindowResize() { this.$nextTick(() => { this.camera.aspect = window.innerWidth / window.innerHeight; this.camera.updateProjectionMatrix(); this.render(); this.renderer.setSize(window.innerWidth, window.innerHeight); }); }, animate() { //更新控制器 this.render(); //更新性能插件 requestAnimationFrame(this.animate); }, //计时器 interval() { if (this.timer) { clearInterval(this.timer); this.timer = null; } //控制分钟增加 this.timer = setInterval(() => { if (this.MS >= 60) { this.MS = 1; this.HS = this.HS + 1; } else { this.MS += this.timerNum; } if (this.HS >= this.HL && this.MS >= this.ML) { this.HS = this.sunrise.slice(0, 2) * 1; this.MS = this.sunrise.slice(3, 5) * 1; } // 更新时间 this.date = new Date( this.date.getFullYear(), this.date.getMonth(), this.date.getDate(), this.HS, this.MS ); // 更新光照的方向和颜色 this.SunCalcGetPosition(this.date); //计算相差时间比 //进度条右侧时间 const timeDifference2 = this.calculateTimeDifference( this.sunrise, this.HS + ":" + this.MS ); let baifenb = ( (timeDifference2.hours * 60 + timeDifference2.minutes) / (this.timeDifference.hours * 60 + this.timeDifference.minutes) ).toFixed(2) * 100; let currentTimeEle = document.querySelector(".current-time"); currentTimeEle.innerHTML = (this.HS < 10 ? "0" + this.HS : this.HS) + ":" + (this.MS < 10 ? "0" + this.MS : this.MS); //设置阴影滚动条长度 this.vanSliderBar.style.width = ((this.vanSlider.clientWidth - this.SunIcon.clientWidth) * baifenb) / 100 + "px"; this.SunIcon.style.left = ((this.vanSlider.clientWidth - this.SunIcon.clientWidth) * baifenb) / 100 + "px"; }, this.intervalNum); }, //点击播放按钮 bfBtnFun() { console.log(this.isPlay); if (this.isPlay) { clearInterval(this.timer); this.timer = null; this.isPlay = false; } else { this.interval(); this.isPlay = true; } }, //计算两时间差 calculateTimeDifference(startTime, endTime) { const start = new Date(); const end = new Date(); // 将时间字符串转换为 Date 对象 const [startHour, startMinute] = startTime.split(":"); const [endHour, endMinute] = endTime.split(":"); start.setHours(startHour, startMinute, 0); end.setHours(endHour, endMinute, 0); // 计算时间间隔(毫秒为单位) const timeDiff = end.getTime() - start.getTime(); // 将毫秒转换为小时和分钟 const hours = Math.floor(timeDiff / (1000 * 60 * 60)); const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60)); return { hours, minutes, }; }, //计算节气日期 jq(num) { var solarTerm = new Array("小寒", "春分", "夏至", "秋分", "立冬", "冬至"); var sTermInfo = new Array(0, 107014, 240693, 375494, 440795, 504758); function sTermDate(y, n) { return new Date( 31556925974.7 * (y - 1900) + sTermInfo[n] * 60000 + Date.UTC(1900, 0, 6, 2, 5) ); } //获取2013年第一个节气小寒的公历日期 var Y = new Date().getFullYear(); var sterm = sTermDate(Y, num); if (this.timer) { clearInterval(this.timer); this.timer = null; } this.isPlay = true; this.date = sterm; this.SunCalcGetTimes(); }, //改变日期 changDate(e) { var that = this; var date = new Date(e); var y = date.getFullYear(); // 年 var m = date.getMonth() + 1; // 月 m = m < 10 ? "0" + m : m; var d = date.getDate(); // 日 d = d < 10 ? "0" + d : d; that.date = y + "/" + m + "/" + d; //拼在一起 this.SunCalcGetTimes(); this.isPlay = true; }, //日期选择器获取焦点 focusDate() { //禁止软键盘弹出 document.activeElement.blur(); clearInterval(this.timer); this.timer = null; }, }, }; </script> <style rel="stylesheet/scss" lang="scss" scoped> ::v-deep .el-date-editor.el-input, .el-date-editor.el-input__inner { width: 150px; } #sunshine { position: relative; } @media only screen and (min-width: 500px) { .bottom-card { position: absolute; top: auto; bottom: 0; left: 50%; width: 100vw; transform-origin: center bottom; transform: translateX(-50%) scale(0.46); } } .bottom-card { position: relative; top: -4.26667vw; padding: 4.26667vw 4vw; background: #fff; border-radius: 4.26667vw 4.26667vw 0 0; z-index: 999; color: #333330; } #app2 { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; color: #333; overflow: hidden; font-size: 3.73333vw; } .page-wrapper { position: relative; height: 100vh; overflow: hidden; display: flex; flex-flow: column nowrap; background: #fff; } .canvas-container { width: 100vw !important; flex: 1; } .bottom-card { position: relative; top: -4.26667vw; padding: 4.26667vw 4vw; background: #fff; border-radius: 4.26667vw 4.26667vw 0 0; z-index: 999; color: #333330; } .bottom-card .top { display: flex; justify-content: space-between; line-height: 4.26667vw; } .bottom-card .top .title { flex: 1; font-size: 4.8vw; font-family: PingFangSC-Semibold, PingFang SC; font-weight: 600; color: #333330; line-height: 6.66667vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .bottom-card .top .date { display: flex; justify-content: flex-end; align-items: center; font-size: 3.73333vw; } .top .date .text { margin-right: 0.8vw; } .bottom-card .time-wrapper { display: flex; flex-flow: row nowrap; align-items: center; justify-content: center; line-height: 5.33333vw; } .bottom-card .time-wrapper .play-btn { width: 9.86667vw; height: 9.86667vw; font-size: 0; margin-left: -1.6vw; border: 1px solid #ff5d5d; border-radius: 50%; box-shadow: 0px 0px 5px #ffa2a2; text-align: center; line-height: 9.86667vw; } .bottom-card .time-wrapper .play-btn svg { vertical-align: middle; } .van-slider { position: relative; width: 100%; height: 2px; background-color: #ebedf0; border-radius: 999px; cursor: pointer; } .bottom-card .time-wrapper .time-slider { flex: 1; display: flex; align-items: center; padding: 0 1.06667vw; font-size: 3.73333vw; } .bottom-card .time-wrapper .time-slider .van-slider { margin: 0 2.66667vw; } .bottom-card .time-wrapper .time-slider .van-slider .custom-button { display: flex; align-items: center; justify-content: center; } .bottom-card .time-wrapper .time-slider .van-slider .custom-button .sun-icon { width: 4.26667vw; height: 4.26667vw; } .bottom-card .time-wrapper .current-time { width: 19.46667vw; height: 9.86667vw; line-height: 9.86667vw; text-align: center; font-size: 3.2vw; font-family: PingFangSC-Semibold, PingFang SC; font-weight: 600; color: #f44; margin-right: -1.6vw; background: url(data:image/png;base64,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); background-size: 100% 100%; } .bottom-card .time-labels { position: relative; width: 65.86667vw; margin-left: 8.26667vw; font-size: 3.2vw; text-align: center; } .bottom-card .time-labels .rise-time { position: absolute; left: -0.8vw; } .bottom-card .time-labels .set-time { position: absolute; right: -0.8vw; } .bottom-card .times { display: flex; flex-flow: row nowrap; justify-content: space-around; } .bottom-card .times .value { margin-left: 1.06667vw; color: #ff3a3a; } .bottom-card .dates { display: flex; justify-content: space-between; margin-top: 4vw; } .bottom-card .dates .btn { flex: 1; line-height: 6.4vw; font-size: 3.46667vw; text-align: center; background: #eee; border-radius: 1.06667vw; color: #333330; border: none; padding: 0; margin: 0 1.6vw; } .bottom-card .dates .btn:first-child { margin-left: 0; } .bottom-card .dates .btn:last-child { margin-right: 0; } .bottom-card .dates .active { color: #fff; background: #f44; } .compass { position: absolute; top: 5.33333vw; left: 5.33333vw; font-size: 0; border-radius: 50%; overflow: hidden; z-index: 999; } .compass .icon { width: 18.13333vw; height: 18.13333vw; } .van-calendar { padding-bottom: 6.4vw; } .ipx .bottom-card { padding-bottom: 8.53333vw; } .ipx .van-calendar { padding-bottom: 13.33333vw; } .user-guide { position: absolute; top: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.7); display: flex; flex-flow: column; align-items: center; justify-content: center; z-index: 1000; } .user-guide .touch-guide { width: 44.26667vw; height: 42.66667vw; } .user-guide .close-btn { width: 43.2vw; height: 15.33333vw; margin-top: 5.33333vw; } .css2DLabel { position: relative; font-size: 2.4vw; line-height: 4vw; border-radius: 0.53333vw; color: #fff; padding: 0 1.06667vw; background: rgba(0, 0, 0, 0.4); } .css2DLabel:after { content: ""; position: absolute; top: 100%; left: 50%; width: 1px; height: 4.26667vw; border: none; background: linear-gradient(180deg, #fff, hsla(0, 0%, 100%, 0) 80%); } @media only screen and (min-width: 500px) { #app { font-size: 24px; } .page-wrapper { display: block; } .canvas-container { width: 100vw !important; height: 100vh !important; } .bottom-card { position: absolute; top: auto; bottom: 0; left: 50%; width: 100vw; transform-origin: center bottom; transform: translateX(-50%) scale(0.46); } .compass { top: 20px; left: 20px; } .compass .icon { width: 80px; height: 80px; } .css2DLabel { font-size: 12px; line-height: 1.5; border-radius: 4px; padding: 0 6px; } .css2DLabel:after { width: 1px; height: 16px; } } @media only screen and (min-width: 1200px) { .bottom-card { transform: translateX(-50%) scale(0.33); } .bottom-card .time-wrapper .time-slider .van-slider .custom-button { padding: 0; } } .loading { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 9999; background-color: #313538; } .loading:before { content: ""; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 2.66667vw; width: 2.13333vw; height: 2.13333vw; color: #fff; border-radius: 50%; text-indent: -9999em; animation: load-effect 1s linear infinite; } @keyframes load-effect { 0% { box-shadow: 0 -3em 0 0.2em #fff, 2em -2em 0 0 #fff, 3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 0 #fff; } 12.5% { box-shadow: 0 -3em 0 0 #fff, 2em -2em 0 0.2em #fff, 3em 0 0 0 #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff; } 25% { box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 0 #fff, 3em 0 0 0.2em #fff, 2em 2em 0 0 #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff; } 37.5% { box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 0 #fff, 2em 2em 0 0.2em #fff, 0 3em 0 0 #fff, -2em 2em 0 -0.5em #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff; } 50% { box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 -0.5em #fff, 2em 2em 0 0 #fff, 0 3em 0 0.2em #fff, -2em 2em 0 0 #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 -0.5em #fff; } 62.5% { box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 0 #fff, -2em 2em 0 0.2em #fff, -3em 0 0 0 #fff, -2em -2em 0 -0.5em #fff; } 75% { box-shadow: 0 -3em 0 -0.5em #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 0 #fff, -3em 0 0 0.2em #fff, -2em -2em 0 0 #fff; } 87.5% { box-shadow: 0 -3em 0 0 #fff, 2em -2em 0 -0.5em #fff, 3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 0 #fff, -3em 0 0 0 #fff, -2em -2em 0 0.2em #fff; } to { box-shadow: 0 -3em 0 0.2em #fff, 2em -2em 0 0 #fff, 3em 0 0 -0.5em #fff, 2em 2em 0 -0.5em #fff, 0 3em 0 -0.5em #fff, -2em 2em 0 -0.5em #fff, -3em 0 0 -0.5em #fff, -2em -2em 0 0 #fff; } } @media only screen and (min-width: 500px) { .loading:before { font-size: 12px; width: 12px; height: 12px; } .bottom-card .time-wrapper .time-slider .van-slider .custom-button { padding: 0; } ::v-deep .el-date-editor.el-input, .el-date-editor.el-input__inner { transform: scale(2.5); } .bottom-card .top .title { flex: none; } .bottom-card .top .date { margin-right: 100px; } } @media only screen and (min-width: 300px) and (max-width: 800px) { #sunshine .page-wrappr { position: absolute; bottom: -20px; border-radius: 20px 20px 0 0; width: 100%; } } .bottom-card .time-wrapper .play-btn svg { width: 4.8vw; height: 4.8vw; } .bottom-card .time-wrapper .time-slider .van-slider .custom-button { padding-left: 0; padding-right: 0; } </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。