这里提供两个工具一个是fastStone Capture 录视频的 https://ezgif.com/video-to-gif
将录制的wmv格式转换gif格式
实现3d切换显示不同模型
这里单单是vue的写法 自己后期可以封装
//首先引入这些文件,当然你得先下载three, npm i three -S
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader";
const dataList = [
{
path: "/3d/小苗-水稻.fbx",
position: { y: -15 },
offset: -100,
},
{
path: "/3d/水稻-拔节.fbx",
position: { y: -25 },//调解上下
offset: 60,//调解大小近大远小
},...这里我就不写过多的为了方便观看 首先path 是模型路径放在public/3d下面,这里用的是fbx模型
];
const zoom = 100;//层级
var timer = null;//timerid
var d3fbxRef = null;//dom元素
var my_three = {
scene: null,
camera: null,
renderer: null,
controls: null,
file: null
};//three的属性,场景相机渲染器等等
var modelList = [];//缓存数据
var list = [];//数据
var oldIndex = null;//切换前的索引
//执行函数初始化
mounted() {
this.init({ models: dataList, targetDom: this.$refs.d3Ref })
},
//下面是封装的
methods: {
//初始化传入2个参数,数据必须是数组,dom
init({ models, targetDom }) {
d3fbxRef = targetDom;//dom元素
list = [...models];//数据
let _this = this;
(async function init() {
await _this.initScene();//初始化场景
await _this.initCamera();//初始化相机
await _this.initModel();//初始化模型
await _this.initRenderer()//初始化渲染器
await _this.initControls();//初始化控制器
})();
},
initScene() {
return new Promise((resolve, reject) => {
my_three.scene = new THREE.Scene();//创建3d场景
resolve();
});
},
initCamera(index = 0) {
//添加各种光照
const { scene, camera } = my_three;
// 环境光
const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
// 平行光
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
// 半球光
const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x00ff00);
//聚光灯
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.position.set(1000, 1010, 1000);
// 点光源
const pointLight = new THREE.PointLight(0xffffff, 0.5, 10, 2);
//场景的内容
if (scene.children.length === 0) {
scene.add(spotLight);
scene.add(hemisphereLight);
scene.add(directionalLight);
scene.add(ambientLight);
}
const { offset = 0 } = list[index];
return new Promise((resolve, reject) => {
// THREE.PerspectiveCamera 透视摄像机
// fov — 摄像机视锥体垂直视野角度
// aspect — 摄像机视锥体长宽比
// near — 摄像机视锥体近端面
// far — 摄像机视锥体远端面
//创建相机
if (camera) {
my_three.camera.position.set(1000, 1000, 1050 + offset);
} else {
my_three.camera = new THREE.PerspectiveCamera(45, 1, 1, 2000);
my_three.camera.position.set(1000, 1000, 1050 + offset);
scene.add(my_three.camera);
}
resolve();
});
},
//初始化模型
initModel(index = 0) {
const { scene } = my_three;
const { path, position } = list[index] || {};
//如果是gltf模型
if (path.includes(".gltf")) {
const loader = new GLTFLoader();
return new Promise((resolve, reject) => {
let oldFile = modelList[index];
oldIndex = index;
if (oldFile) {
my_three.file = oldFile;
scene.add(oldFile);
resolve();
} else {
loader.load(path, ({ scene: objectScene }) => {
const { y } = position || { y: 0 };
objectScene.name = path;
objectScene.position.set(1000, 1000 + y, 1000);
objectScene.traverse((child) => {
// 模型转到一定角度消失 DoubleSide 解决
if (child instanceof THREE.Mesh) {
child.material.side = THREE.DoubleSide;
}
});
modelList[index] = objectScene;
my_three.file = objectScene;
scene.add(objectScene);
resolve();
});
}
});
} else {
//fbx模型
const loader = new FBXLoader();
return new Promise((resolve, reject) => {
//这里将显示过得文件缓存起来了,防止2次渲染卡
let oldFile = modelList[index];
oldIndex = index;
if (oldFile) {
my_three.file = oldFile;
scene.add(oldFile);
resolve();
} else {
loader.load(path, (object) => {
const { y } = position || { y: 0 };
object.name = path;
object.position.set(1000, 1000 + y, 1000);
object.traverse((child) => {
// 模型转到一定角度消失 DoubleSide 解决
if (child instanceof THREE.Mesh) {
child.material.side = THREE.DoubleSide;//显示2个面默认背面不显示
// child.material.side = 2;
}
});
modelList[index] = object;
my_three.file = object;
scene.add(object);
resolve();
});
}
});
}
},
initRenderer() {
//下面都是些渲染配置项
const { scene, camera, file } = my_three;
return new Promise((resolve, reject) => {
my_three.renderer = new THREE.WebGLRenderer({
alpha: true, // 画布是否包含alpha(透明度)缓冲区。默认值为false。
antialias: true, // 是否执行抗锯齿。默认值为false。
});
// 定义渲染器的输出编码。默认为THREE.LinearEncoding
my_three.renderer.outputEncoding = THREE.sRGBEncoding;
// 设置设备像素比。通常用于避免HiDPI设备上绘图模糊
my_three.renderer.setPixelRatio(window.devicePixelRatio);
// 设置alpha(背景透明度)。合法参数是一个0.0到 1.0之间的浮点数
my_three.renderer.setClearAlpha(0);
// 将输出canvas的大小调整为(width, height)并考虑设备像素比,且将视口从(0, 0)开始调整到适合大小
my_three.renderer.setSize(d3fbxRef.clientWidth, d3fbxRef.clientHeight);
// my_three.renderer.domElement: canvas,渲染器在其上绘制输出。
d3fbxRef.appendChild(my_three.renderer.domElement);
// 开始渲染
my_three.renderer.render(scene, camera);
resolve();
});
},
initControls() {
const { scene, camera, renderer, file } = my_three;
return new Promise((resolve, reject) => {
my_three.controls = new OrbitControls(camera, renderer.domElement);
my_three.controls.target.set(1000, 1000, 1000);
my_three.controls.maxZoom = 2;
my_three.controls.minZoom = 0.5;
// my_three.controls.enableZoom = false;
// console.log(my_three.controls);
// 开启自动旋转
my_three.controls.autoRotate = true;
my_three.controls.enableDamping = true//阻尼效果
// 旋转速度,默认为20
my_three.controls.autoRotateSpeed = 10;
// 更新控制器
my_three.controls.update();
// my_three.controls.addEventListener('change', changeControls)
// function changeControls(event) {
// console.log(camera.position)
// }
const render = function () {
cancelAnimationFrame(timer);
my_three.controls.enableZoom = Math.abs(camera.position.z - file.position.z) < zoom;
my_three.renderer.render(scene, camera);
my_three.controls.update();
// 请求动画帧。
// 与setTimeout相比,requestAnimationFrame最大的优势是由系统来决定回调函数的执行时机。
// 具体一点讲,如果屏幕刷新率是60Hz,那么回调函数就每16.7ms被执行一次,如果刷新率是75Hz,那么这个时间间隔就变成了1000/75=13.3ms,
// 换句话说就是,requestAnimationFrame的步伐跟着系统的刷新步伐走。
// 它能保证回调函数在屏幕每一次的刷新间隔中只被执行一次,这样就不会引起丢帧现象,也不会导致动画出现卡顿的问题。
timer = requestAnimationFrame(render);
};
render();
resolve();
});
},
clearModel(index) {
const { scene, renderer } = my_three;
return new Promise((resolve, reject) => {
// 通过模型名称找到对应的模型变量
let obj = scene.getObjectByName(list[index]["path"]);
// 删除场景中的模型
obj && scene.remove(obj);
resolve();
});
},
//切换模型 参数索引
async changeModel(index) {
await this.clearModel(oldIndex);
await this.initCamera(index);
await this.initModel(index);
await this.initControls();
},
//模型清除
clearAll() {
const { scene, renderer, controls } = my_three;
cancelAnimationFrame(timer);
renderer.forceContextLoss();
renderer.dispose();
controls.dispose();
scene.clear();
my_three = {
scene: null,
camera: null,
renderer: null,
controls: null,
file: null,
};
}
}