前段时间看到懂车帝app有几个效果非常好的汽车展示方面的模块,再加上前年在公司写过一个关于汽车售后业务选择汽车部位的组件,就开始着手把这几个组件编写成一个组件库发布出去供大家使用,当然提供按需引入方式,使用的依然是vuecli3工具其中的vue-cli-service build --target lib做为打包工具,下面会讲如何把打包成独立的js 然后供大家可以选择按需引入的方式减少文件体积,由于保密协议的原因,可能这个项目暂时不能开源,以后会开源,但是我尽量把每个组件的关键思路提供出来。图片资源部分引用的是懂车帝和乐车的,如有侵权请联系我删除。
vuecli3
通过vuecli3创建一个初始化项目,具体可以查看官网。cli.vuejs.org/zh/guide/cr…
我们需要一个文件夹存放组件,一个文件夹存放示例,按照以下方式对目录进行改造。
src目录更名为examples,导致项目无法运行
注:vue.config.js 是一个可选的配置文件,如果项目的 (和 package.json 同级的) 根目录中存在这个文件,那么它会被 @vue/cli-service 自动加载。
重新配置入口,修改配置中的 pages 选项
新版 Vue CLI 支持使用 vue.config.js 中的 pages 选项构建一个多页面的应用。
这里使用 pages 修改入口到 examples,设置css不单独提取。
module.exports = { // 修改 src 目录 为 examples 目录 pages: { index: { entry: 'examples/main.js', template: 'public/index.html', filename: 'index.html' } }, css: { extract: false } } 复制代码
以上我们已配置好对新目录架构的支持,接下来我们尝试编写组件库。我大概说下流程:
<template> <div class="showCar360degrees"> <div class="image-container" > <div class="cpt-wg-360" @touchmove = touchmove @touchstart = touchstart :style="{ filter: filterblur }"> <div class="wg-item" v-for="(item,index) in imgs" :key="item" > <img v-if="index == 0" style="opacity: 1;" class="wg-img" :src=item /> <img v-else style="opacity: 0;" class="wg-img" :src=item /> </div> </div> <span v-show = percentageflag class="loading-tip">{{percentage}}%</span> </div> </div> </template> <script> export default { name: "showCar360degrees", data() { return { eventtouches:{}, percentage:0, percentageflag:true, filterblur: 'blur(2px)' }; }, props: { //图片数组 imgs: Array, //速度调节 speed:{ type: Number, default: 20 }, }, mounted(){ this.calculatepercentage() }, methods: { calculatepercentage(){ let imgele = document.getElementsByClassName('wg-img') if(imgele.length>0){ let num = 100 / imgele.length num = Math.ceil(num) for(let i = 0;i<imgele.length;i++){ imgele[i].onload = ()=>{ this.percentage += num if(this.percentage >= 100){ this.percentage = 100 setTimeout(() => { this.percentageflag = false this.filterblur = 'blur(0px)' }, 100); } } } } }, touchstart(e){ this.eventtouches = e; }, touchmove(e){ if((e.timeStamp - this.eventtouches.timeStamp)>this.speed){ if((e.touches[0].clientX - this.eventtouches.touches[0].clientX)>0 ){ this.right() }else { this.left() } this.eventtouches = e; } }, left(){ let domlist = Array.from( document.getElementsByClassName('wg-img')); for(let i= 0; i < domlist.length; i++){ if(domlist[i].style.opacity == '1'){ domlist[i].setAttribute('style', 'opacity: 0;'); let j = i == 0 ? domlist.length-1: i-1 domlist[j].setAttribute('style', 'opacity: 1;'); return } } }, right(){ let domlist = Array.from( document.getElementsByClassName('wg-img')); for(let i= 0; i < domlist.length; i++){ if(domlist[i].style.opacity == '1'){ domlist[i].setAttribute('style', 'opacity: 0;'); let j = i == domlist.length-1 ? 0: i+1 domlist[j].setAttribute('style', 'opacity: 1;'); return } } } }, }; </script> <style scoped> .image-container { display: flex; justify-content: center; position: relative; width: 100%; height: 160px; background-image: linear-gradient(0deg, hsla(0, 0%, 100%, 0), #ccc); } .cpt-wg-360 { width: 100%; height: 100%; position: relative; min-height: 160px; overflow: hidden; } .cpt-wg-360 .wg-item { position: absolute; left: 50%; transform: translateX(-50%); width: 100%; height: 100%; padding: 0 9.6%; box-sizing: border-box; margin-top: -15px; } .cpt-wg-360 .wg-img { width: 100%; } .image-container .loading-tip { width: 40px; height: 40px; border-radius: 50%; border: 1px solid #ffe100; background: rgba(0,0,0,.5); color: #fff; font-size: 10px; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); display: flex; align-items: center; justify-content: center; } img { border-style: none; } </style> 复制代码
<template> <div class="pano"> <div id="container" ></div> </div> </template> <script> /* eslint-disable */ import * as THREE from "three"; export default { name: "pano", data() { return { camera: null, scene: null, renderer: null, mesh: null, onMouseDownMouseX : 0, onMouseDownMouseY : 0, lon : 180, onMouseDownLon : 0, lat : 0, onMouseDownLat : 0, phi : 0, theta : 0, isUserInteracting:false }; }, props: { imgurl:{ type:String, default:'' } }, mounted() { this.init(); this.animate(); this.addEvent(); }, methods: { addEvent(){ this.$el.addEventListener('touchstart', this.start, false) this.$el.addEventListener('touchmove', this.move, false) this.$el.addEventListener('touchend', this.end, false) this.$el.addEventListener('mousedown', this.start, false) this.$el.addEventListener('mousemove', this.move, false) this.$el.addEventListener('mouseup', this.end, false) }, init: function() { let container = document.getElementById("container"); let opt={ fov:90, width:container.clientWidth, height:container.clientHeight, } //创建相机 this.camera = new THREE.PerspectiveCamera( opt.fov, opt.width / opt.height, 1, 10000 ); this.camera.target = new THREE.Vector3(0, 0, 0); //指定看向哪个点 //创建几何体 let geometry = new THREE.SphereBufferGeometry(60, 60, 60); geometry.scale(-1, 1, 1); // 贴图朝里 // 加载贴图 let texture = new THREE.TextureLoader().load( // "" this.imgurl ); //创建 let material = new THREE.MeshBasicMaterial({ map: texture }); this.mesh = new THREE.Mesh(geometry, material); this.scene = new THREE.Scene(); this.scene.add(this.mesh); this.renderer = new THREE.WebGLRenderer({ antialias: true }); this.renderer.setSize(opt.width, opt.height); container.appendChild(this.renderer.domElement); }, start(event){ this.isUserInteracting = true; var clientX = event.clientX || event.touches[0].clientX; //获取点击开始时的屏幕位置 var clientY = event.clientY || event.touches[0].clientY; this.onMouseDownMouseX = clientX; this.onMouseDownMouseY = clientY; this.onMouseDownLon = this.lon; //经度 this.onMouseDownLat = this.lat; //纬度 }, move(event){ //算出经度纬度移动的距离 if(this.isUserInteracting ){ var clientX = event.clientX || event.touches[0].clientX; var clientY = event.clientY || event.touches[0].clientY; this.lon = (this.onMouseDownMouseX - clientX ) * 0.2 + this.onMouseDownLon; this.lat = ( clientY - this.onMouseDownMouseY ) * 0.2 + this.onMouseDownLat; } }, end(event){ this.isUserInteracting = false; }, animate: function() { requestAnimationFrame(this.animate); this.lon += 0.1 this.lat = Math.max(-85, Math.min(85, this.lat)); this.phi = THREE.Math.degToRad(90 - this.lat); this.theta = THREE.Math.degToRad(this.lon); this.camera.target.x = Math.sin(this.phi) * Math.cos(this.theta); this.camera.target.y = Math.cos(this.phi); this.camera.target.z = Math.sin(this.phi) * Math.sin(this.theta); this.camera.lookAt(this.camera.target); this.renderer.render(this.scene, this.camera); } } }; </script> <style scoped> #container { width: 100vw; height: 100vh; } </style> 复制代码
<template> <div id="app"> <carMaintenanceModel :carpart = carpart v-on:onChange=carChange></carMaintenanceModel> <showCar360degrees :speed = 20 :imgs = imgs ></showCar360degrees> <pano imgurl = 'https://p9-dcd.byteimg.com/img/mosaic-legacy/bef400005f4eca28e99e~tplv-resize:4000:0.image'></pano> </div> </template> <script> import Vue from 'vue' //全部引用 // import carmodelcomponents from 'carmodelcomponents' // Vue.use(carmodelcomponents) //按需引用 import carMaintenanceModel from 'carmodelcomponents/lib/carMaintenanceModel.js' import showCar360degrees from 'carmodelcomponents/lib/showCar360degrees.js' import pano from 'carmodelcomponents/lib/pano.js' Vue.use(carMaintenanceModel) Vue.use(showCar360degrees) Vue.use(pano) export default { name: 'App', components: { }, data(){ return{ imgs:[ "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef30000e03696639164~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef30000e0358a61e148~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef10000e2c28d75f043~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef10000e2c10123ffe2~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef4000065de9346a35d~tplv-resize:640:0.png", "https://p9-dcd.byteimg.com/img/mosaic-legacy/bef20000e5258c0eed4e~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/beef0000e308f1415db3~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef10000e2c510c0b6c0~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef20000e52661db22a3~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef4000065ea77e140ae~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef10000e2c6437dd4d3~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef4000065e9893b1aec~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef4000065e6ff33e833~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef4000065e81a11bb83~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/9b1300016b2ee29ca41e~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef4000065e7cbeb7f66~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef4000065e5ccb3bda9~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef10000e2c460481ef9~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef4000065e46f39caa7~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef30000e038c8917999~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef4000065e321519d3a~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef4000065e234fc8580~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/beef0000e307938c2601~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef00000e2f05c5b3d90~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef10000e2c313c5c2b8~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/9b1300016b2d7cb46459~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/9b1300016b2b3c80acdf~tplv-resize:640:0.png", "https://p9-dcd.byteimg.com/img/mosaic-legacy/bef20000e5248ce4558b~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef4000065e129d49497~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/bef4000065e060e466a2~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/9b1300016b2cf2fcd008~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef4000065df1aabda54~tplv-resize:640:0.png", "https://p1-dcd.byteimg.com/img/mosaic-legacy/bef20000e5222d9c040b~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef20000e523c6b69e37~tplv-resize:640:0.png", "https://p3-dcd.byteimg.com/img/mosaic-legacy/bef30000e037e7ee5961~tplv-resize:640:0.png", "https://p6-dcd.byteimg.com/img/mosaic-legacy/beef0000e30646247719~tplv-resize:640:0.png" ], carpart: ['component-1', 'component-2'], } }, methods:{ carChange(e){ console.log(e); }, } } </script> <style> body{ margin: 0; } #app { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; color: #2c3e50; } </style> 复制代码
至此这篇文章就结束了 如果还有什么问题评论区留言,我会更改,有好的需求也可以提,如果需求合理并且在我能力范围内,我会加上去。希望看完别忘记点赞,全布手敲。 后面就是npm发包了,如有需要可以关注我上一篇文章 => juejin.im/post/5dcfb1…