Some time ago, I saw that There were several effective modules of car display in The App of Understand Car Emperor. In addition, I wrote a component about car parts selection in the company the year before last, so I began to write these components into a component library and publish them for everyone to use. Of course, the introduction method on demand is provided. Vuecli3 tool vue-cli-service build –target lib as a package tool, the following will talk about how to package into independent JS so that you can choose to import on demand to reduce the size of the file, due to the confidentiality agreement, This project may not be open source for the time being, but I will try to provide the key ideas for each component. Part of the picture resources quoted is to understand the car emperor and le car, if there is infringement, please contact me to delete.

I. Technology stack

vuecli3

Second, the effect demonstration

Create a project

Create an initialization project with Vuecli3. See the website for details. Cli.vuejs.org/zh/guide/cr…

Fourth, adjust the project structure

We need a folder for components, a folder for example, and modify the directory as follows.

  • Examples // Change the SRC directory to examples to show examples
  • Packages // Add Packages for writing storage components, as shown in my project structure

Configure the project to support the new directory structure

The SRC directory was renamed to examples, causing the project to fail to run

  • Note: Vue.config. js is an optional configuration file that will be automatically loaded by @vue/cli-service if it exists in the root directory of the project (the same as package.json).

  • Reconfigure the entry and modify the Pages option in the configuration

  • The new Vue CLI supports building a multi-page application using the Pages option in vue.config.js.

  • Here use pages to modify the entry to examples and set CSS not to be extracted separately.

module.exports = {
  // Change the SRC directory to examples
  pages: {
    index: {
      entry: 'examples/main.js'.template: 'public/index.html'.filename: 'index.html'}},css: { extract: false}}Copy the code

Write component library box and configure on-demand packaging command

Now that we’ve configured support for the new directory schema, let’s try writing component libraries. Let me outline the process:

  • In the Packages directory, all individual components are stored as folders, so create folders carMaintenanceModel, showCar360degrees, and Pano for the three components previewed above
  • Create a SRC folder under each folder to store the component’s source code
  • Create an index.js file in each folder to provide references to the component, as shown in an example
  • Create a unified entry file for components/Packages /index.js to export all components
  • If you want to package all the components separately, you need to do some additional configuration:
    1. The root directory to create build/webpack.com ponent. Js
    2. The root directory creates the components.json file
    3. Package. json configure the package command in lib mode component named CarModelComponents output directory lib

      At this point, the overall framework is out, the packaged JS can be seen in the picture below, delete the rest of the useless files, there are three separate component JS and a whole.umd.min.js file

Seven, choose the general idea of car parts

  1. First of all, this is definitely not a 3D model, it’s controlled by four background images display: None; To display the current corresponding image.
  2. Map area is used to obtain the touched part, which can achieve a very detailed specific part.
  3. Define a Map
  4. Then four different model car bearing figure shows part of the graphics is different, but no matter which picture after clicking to obtain parts are the same, certainly this click on the front bumper, for example, by getting the classname name, to control all the figure of the front bumper with front bumper from hidden to display or hide, $emit(“onChange”, [_this.result, _this.arr]); Pass the parts array and the corresponding Chinese name array to the parent component.
  5. This is basically the idea of this component, but there is a lot of code, very fine, here can not say all, later open source everyone see.

Eight, the general idea of 360 degree automobile display

  1. Opacity: 0 o r 1; opacity: 0 O r 1; opacity: 0 O r 1; opacity: 0 O r 1; opacity: 0 O r 1; opacity: 0 O r 1 . The number of pictures of their own control, more delicate display, slightly loss of performance, less experience effect is not good, thrift by people, according to the actual project to make a choice, of course, understand the car emperor is 36, I used the car emperor picture resources, convenient and easy, infringement please contact me to delete.
  2. Note that the picture is not fully loaded to give a picture load percentage and the style of the dummy, the code issued below, this component is written by their amateur, does not involve confidentiality agreements.
<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: {// props: 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, 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>Copy the code

9. General idea of panorama

  1. Here is a basic version of the panorama realized by Three. js, and the principle is relatively simple. However, it is recommended to read the tutorial of Three. js first to understand various concepts, such as scene, geometry, material, camera, renderer and so on
  2. Simple understanding of the principle is to create a sphere, and then to the sphere wall labeled panorama (also understand car emperor panorama, save trouble, delicious), the camera (in fact, the eye) in the sphere, the grid is added to the scene, the scene and the camera is added to the renderer, the final rendering, constantly changing sliding when the camera viewing Angle. The specific code is issued below, written in spare time, and does not involve confidentiality agreements.
<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, PerspectiveCamera = new THREE.PerspectiveCamera(opt.fov, opt.width/opt.height, 1, 10000); this.camera.target = new THREE.Vector3(0, 0, 0); / / specify at what point / / create geometry let geometry = new THREE. SphereBufferGeometry (60, 60, 60); geometry.scale(-1, 1, 1); Let texture = new three.textureLoader ().load(// "" this.imgurl); // create 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; / / get click screen position at the beginning of the var clientY = event. ClientY | | event. Touches [0]. ClientY; this.onMouseDownMouseX = clientX; this.onMouseDownMouseY = clientY; this.onMouseDownLon = this.lon; OnMouseDownLat = this.lat; } / / latitude, move (event) {/ / compute the distance of longitude latitude mobile if (this. IsUserInteracting) {var clientX = event. ClientX | | event. Touches [0]. ClientX;  var clientY = event.clientY || event.touches[0].clientY; This.lon = (this.onMousedownsex-clientx) * 0.2 + this.onMousedownlon; This.lat = (clientY - this.onmousedownsey) * 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>Copy the code

Use of component libraries

  1. Yarn Add CarModelComponents is currently version 1.0 and I may improve the panorama with more features later
  2. The code is posted below and you can choose two ways to introduce it
<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' // all references // import carModelComponents from 'carModelComponents' // Vue. Use (carmodelcomponents) / / on-demand reference 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>Copy the code

Xi. Conclusion

This is the end of this article. If there are any questions left in the comments section, I will change them. If there are good requirements, I can also mention them. Hope you don’t forget to give it a thumbs up. NPM is behind the package, if you need to pay attention to my last article => juejin.cn/post/684490…