一. 先上效果
二. 技术栈
uniapp + g3d.min.js(101kb)
考虑过使用three.js 或者 photo-sphere-viewer.js;但是最终没有使用; 因为如果用这两种js方式实现, 基本上单独写vue的Vr效果项目, 然后小程序项目通过webview跳转vue项目; 写两个项目完成一个六张图的渲染,想想舍弃; 但是后面如果需要一些复杂的交互场景, 可以试试这两个js实现
三. 实现步骤
1. 获取g3d.min.jd
先去 github.com/alibaba/G3D 把项目拉下来, 然后cnpm i; 然后npm run build; 得到一个dist 文件夹; 里面有g3d.min.js; 才101kb;
2. 新建ImageAr.js
新建一个 ImageAr.js, 代码如下
let {
Engine,
Scene,
RotatePerspectiveCamera,
Skybox,
Geometry
} = require("./g3d.min.js")
let lx = null,
ly = null;
class ImageAr {
/**
* @param {Object} selector
* @param {Object} canvas
* @param {Object} images
* @param {Object} opt
*/
constructor(canvas, images, opt) {
this.canvas = canvas;
// this.gl = canvas.getContext('webgl');
this.images = images;
this.opt = opt || {
//贴图全部加载完成的回调事件
textureComplete: (image360) => {}
}
this.init();
}
/**
* 初始化
*/
init() {
let self = this;
this.engine = new Engine(self.canvas);
this.scene = new Scene(this.engine);
this.camera = new RotatePerspectiveCamera(this.scene);
//横向角度
this.camera.alpha = 0;
//纵向角度
this.camera.beta = 0;
this.camera.radius = 10;
this.camera.near = 0.001;
this.camera.far = 2000;
let cnt = 0;
// fbudlr
let imageListPOsi = [ "back","front", "top","bottom", "left", "right"]; // 注意图片顺序 后, 前, 上, 下, 左, 右
let pos = {}
for (let i = 0; i < this.images.length; i++) {
let image = this.canvas.createImage(); //和h5区别 小程序这里没有 new Image
pos[imageListPOsi[i]] = image;
image.onload = () => {
cnt++;
if (cnt >= 6) { //pano形式最多6张,也就是正方体的6个面
let skybox = new Skybox(self.scene, pos,100,true);
console.log(skybox)
function render() {
self.scene.render();
self.requestAnimationFrame(render);
}
render();
//贴图全部加载完成后的回调,外部调用
self.opt.textureComplete && self.opt.textureComplete(self);
}
}
image.src = this.images[i];
}
}
touchmove(e) {
let self = this;
//console.log(e)
let x = e.touches[0].clientX;
let y = e.touches[0].clientY;
self.camera.alpha += (x - lx) / 5;
this.camera.beta = self.clamp(-90, 90, this.camera.beta - (y - ly) / 5);
lx = x;
ly = y;
}
touchstart(e) {
let self = this;
//console.log(e)
let x = e.touches[0].clientX;
let y = e.touches[0].clientY;
lx = x;
ly = y;
}
touchend(e) {
}
/**
* 重绘界面 就认为是动画呈现吧
* 见 https://developers.weixin.qq.com/miniprogram/dev/api/canvas/Canvas.requestAnimationFrame.html
*/
requestAnimationFrame(cb) {
this.canvas.requestAnimationFrame(cb);
}
clamp(min, max, v) {
return v < min ? min : v > max ? max : v;
}
}
export default ImageAr
3. 新建一个index.vue文件, 代码如下
<template>
<view class="bui-vr-preview" style="background-color: aliceblue;">
<canvas type="webgl" :id="id" :canvas-id="id" :style="{width: width, height:height}" :disable-scroll="true"
@touchmove.prevent.stop="touchmove" @touchstart.prevent.stop="touchstart" @touchend.prevent.stop="touchend"
@error="canvasIdErrorCallback"></canvas>
</view>
</template>
<script>
// fbudlr
import ImageAr from "./ImageAr.js";
let images = [
'https://cdn.huodao.hk/upload_img/20220620/c34262935511d61b2e9f456b689f5c1c.jpg', // 后
'https://cdn.huodao.hk/upload_img/20220620/722d2bf88f6087800ddf116511b51e73.jpg', // 前
'https://cdn.huodao.hk/upload_img/20220620/273081d1896fc66866842543090916d3.jpg', // 上
'https://cdn.huodao.hk/upload_img/20220620/8747f61fd2215aa748dd2afb6dce3822.jpg', // 下
'https://cdn.huodao.hk/upload_img/20220620/3e532822bd445485d27677ca55a79b10.jpg', // 左
'https://cdn.huodao.hk/upload_img/20220620/cebf6fbcafdf4f5c945e0881418e34ec.jpg', // 右
]
// 后 , 前 , 上, 下, 左, 右
// [ "back","front", "top","bottom", "left", "right"]
// images1 是github中 G3D 的 demo Skybox 中的 6张图片
let images1 = [
'http://gw.alicdn.com/tfs/TB1O4QUAqmWBuNjy1XaXXXCbXXa-1024-1024.png',
'http://gw.alicdn.com/tfs/TB1qZBqATtYBeNjy1XdXXXXyVXa-1024-1024.png',
'http://gw.alicdn.com/tfs/TB1wcxqATtYBeNjy1XdXXXXyVXa-1024-1024.png',
'http://gw.alicdn.com/tfs/TB1O7C5AAyWBuNjy0FpXXassXXa-1024-1024.png',
'http://gw.alicdn.com/tfs/TB16.OnAwmTBuNjy1XbXXaMrVXa-1024-1024.png',
'http://gw.alicdn.com/tfs/TB1zIBqATtYBeNjy1XdXXXXyVXa-1024-1024.png',
]
export default {
props: {
/* canvas id, 同一页面多次引用,不可重名 */
id: {
type: String,
default: "canvas_" + Math.round(Math.random() * 9999)
},
height: {
type: String,
default: "450rpx"
},
/**
* 宽度 默认 100%
*/
width: {
type: String,
default: "100%"
}
},
data() {
return {
imagebox: null,
inited: false,
};
},
onLoad() {
this.$nextTick(() => {
this.init()
})
},
methods: {
init() {
let self = this;
let selector = uni.createSelectorQuery().select(`#${this.id}`);
selector.node()
.exec((res) => {
const canvas = res[0].node
this.imagebox = new ImageAr(canvas, images, {
textureComplete: () => {
self.inited = true;
}
});
})
},
canvasIdErrorCallback: function(e) {
console.error("canvas 操作异常", e.detail.errMsg, e)
},
touchmove(e) {
let self = this;
if (!self.inited) return;
this.imagebox.touchmove(e)
},
touchstart(e) {
let self = this;
this.imagebox.touchstart(e)
},
touchend(e) {
let self = this;
if (!self.inited) return;
this.imagebox.touchend(e)
},
}
}
</script>
<style lang="scss" scoped>
</style>
4. g3d.min.js; ImageAr.js; index.vue放在同一目录下; 运行微信小程序; 到此完成
结束语: 坑1. 一定要注意图片的摆放顺序; 坑2. 一定要使用正确的小程序获取dom节点方式; 最后, 这种ar效果是需求简单的场景, 六张图完成; 如果有复杂的场景, 这个可能不太适合; 最最后, 研究了一晚上,给个鼓励吧