H5使用deviceorientation陀螺仪重力感应事件实现仿淘宝VR图片效果

淘宝VR效果图,晃动手机可以显示底图的不同部分。

我们的要求是页面进来如果不支持VR则播放兜底动画,左右上下晃动效果,支持vr则呈现VR效果,

 

 

注意事项: 1 devicerientation事件仅在https协议下生效,否则看不到效果。

                   2 IOS手机需要手动调用授权方法,DeviceOrientationEvent.requestPermission方法。该方法不能直接调用,也不能通过模拟点击的方式。必须是用户实际与屏幕的click事件。具体参见好好掰扯掰扯,从H5的DeviceMotion事件兼容处理到摇一摇功能实现 - 知乎 (zhihu.com) 这篇文章 ,我们的实现方式是底图上有一个半透明的小tips。安卓展示提示文案,ios提示用户点击进行授权。这里不做叙述。

                   3 IOS还有一个坑是,会自动帮助用户记住授权结果,如果用户点击取消授权,则再次调用授权接口,不会提示授权而是上次的拒绝状态,除非彻底退出APP,浏览器端是清除该网站缓存。

代码实现:

vrimg作为可视窗口,fullimg作为底图,支持多个VR图片。

<div class="vrimg" style="position: absolute;top: 0rem;left: 0rem;width: 3.75rem;height:1.8075rem;border-top-left-radius:0.4rem;border-top-right-radius:0.4rem;border-bottom-left-radius:0.4rem;border-bottom-right-radius:0.4rem;overflow: hidden" >
      <div class="fullimg" style="position: absolute;top: -1.0963rem;left: -1.125rem;width: 6rem;height:4rem;background-image:url(./.图片地址);background-size: cover; background-repeat: no-repeat" ></div>
    </div>

<div class="vrimg" style="position: absolute;top: 0rem;left: 0rem;width: 3.75rem;height:1.8075rem;border-top-left-radius:0.4rem;border-top-right-radius:0.4rem;border-bottom-left-radius:0.4rem;border-bottom-right-radius:0.4rem;overflow: hidden" >
      <div class="fullimg" style="position: absolute;top: -1.0963rem;left: -1.125rem;width: 6rem;height:4rem;background-image:url(./.图片地址);background-size: cover; background-repeat: no-repeat" ></div>
    </div>


js实现

 

window.onload=function(){
//调用初始化图片方法 如果图片配置的是动画则播放动画,如果配置为VR效果 判断是IOS还是安卓 安卓执行重力感应事件,IOS提醒授权
VRIMGInit();
}

//VR图片
//此参数用于页面加载时用户当前角度用于后续计算偏移量
var defaultVRFlag = 0;
var defaultBeta = 0;
var defaultGamma = 0;
//此参数用于计算上一次角度 和一下次角度做对比 剔除角度反转情况
var lastGama = 0;
var lastBeta = 0;
//此标志用于判断ios是否是最开始不用授权的版本
var IOSMoveFlag = 0;
if (window.DeviceOrientationEvent) {window.addEventListener('deviceorientation', handleOrientationVR);}
function handleOrientationVR(event) {
    if (defaultVRFlag < 3) {
        // 1进入页面获取手机初始值 触发很快 因此我设置第三次触发的时候记录手机的beta gamma当作初始角度
        defaultBeta = event.beta;
        defaultGamma = event.gamma;
        defaultVRFlag++;
        IOSMoveFlag++;//如果是早期不用授权版本的IOS flag++
        return;
    } 
// beta接近90° 即手机垂直放置的时候 会出现跳动 判断条件限制
if (!(event.beta > 89 && event.beta < 91)) { //加速度过大 锁定gama 陀 if (Math.abs(lastGama - event.gamma) < 50) { lastGama = event.gamma; var gamma = event.gamma - defaultGamma; if (gamma > 90) { gamma = 90; } if (gamma < -90) { gamma = -90; } MoveVRImg("gamma", gamma, null); } } //加速度过大 锁定beta if (Math.abs(lastBeta - event.beta) < 90) { lastBeta = event.beta; var beta = event.beta - defaultBeta; if (beta > 180) { beta = 180; } if (beta < -180) { beta = -180; } MoveVRImg("beta", null, beta); } } var anmateList = [] function MoveVRImg(type, gamma, beta) { var VRElements = document.querySelectorAll(".VRImg-sensor"); for (var i = 0; i < VRElements.length; i++) { //el为VR图片的容器 var el = VRElements[i]; //底图 var fullimg = el.querySelector(".fullimg "); //执行moveVR的时候取消动画效果 if (anmateList.length > 0) { anmateList.forEach(item => { item.cancel() }) anmateList.length = 0 } if (type == "gamma") { //最大偏移量 var maxX = (fullimg.clientWidth - el.clientWidth); var offsetleft = fullimg.offsetLeft + (maxX * gamma) / 180; if (offsetleft >= 0) { offsetleft = 0; } if (offsetleft <= -maxX) { offsetleft = -maxX; } fullimg.style.left = `${offsetleft}px`; } if (type == "beta") { var maxY = (fullimg.clientHeight - el.clientHeight); var offsettop = fullimg.offsetTop + (maxY * beta) / 360; if (offsettop >= 0) { offsettop = 0; } if (offsettop <= -maxY) { offsettop = -maxY; } fullimg.style.top = `${offsettop}px`; } } } function VRImgInit() { //获取到所有的VR效果图片 if (window.DeviceOrientationEvent) { //初始化tips var VRElements = document.querySelectorAll(".vrimg"); for (var i = 0; i < VRElements.length; i++) { var el = VRElements[i]; var fullimg = el.querySelector(".fullimg "); if (fullimg.getAttribute("vrEffect") == "G-sensor") { //开启重力感应 var vrtip = el.querySelector(".vrtips "); if (isIOSOrAndroid()) { if (IOSMoveFlag > 0) { //IOS12.2以前 直接触发 vrtip.innerHTML = "晃动手机 " + vrtip.getAttribute("iostips"); setTimeout(function (tip) { tip.style.display = "none" }, 2000, vrtip) } else { vrtip.innerHTML = "点击此处 " + vrtip.getAttribute("iostips"); vrtip.onclick = function (e) { e.stopPropagation(); e.currentTarget.style.display = "none"; askDeviceOrientationPermission(); } addAnimate(el, fullimg); } } else { //andord vrtip.innerHTML = "晃动手机 " + vrtip.getAttribute("androdtips"); setTimeout(function (tip) { tip.style.display = "none" }, 2000, vrtip) } } else if (fullimg.getAttribute("vrEffect") == "loop-animated") { //给元素添加动画 addAnimate(el, fullimg); } else { //do nothing } } } else { //页面不支持重力感应,所有元素播放兜底动画 var VRElements = document.querySelectorAll(".vrimg"); VRElements.entries(item => { var fullimg = item.querySelector(".fullimg "); addAnimate(item, fullimg); }) } }
//兜底动画
function addAnimate(el, fullimg) { var animatedArray = [] var dection = fullimg.getAttribute("animationderaction"); if ((dection == 'left-right')) { var offX = (fullimg.clientWidth - el.clientWidth) / 2; animatedArray = [ { transform: 'translateX(0px)' }, { transform: `translateX(${offX}px)` }, { transform: `translateX(${-offX}px)` }, { transform: 'translateX(0px)' } ] } else if ((dection == 'up-down')) { let offY = (fullimg.clientHeight - el.clientHeight) / 2; animatedArray = [ { transform: 'translateY(0px)' }, { transform: `translateY(${offY}px)` }, { transform: `translateY(${-offY}px)` }, { transform: 'translateY(0px)' } ] } var animatedObj = { duration: parseFloat(fullimg.getAttribute("animatespeed")), iterations: 'Infinity', delay: 0, easling: 'linear' } var animateObj = fullimg.animate(animatedArray, animatedObj) if (fullimg.getAttribute("vrEffect") == "G-sensor") { anmateList.push(animateObj); } }
//ios授权
function askDeviceOrientationPermission() { if (typeof DeviceOrientationEvent.requestPermission == "function") { //IOS 13 DeviceOrientationEvent.requestPermission().then(response => { if (response == "granted") { //点击授权 点击提示消失 $(".vrtips").css("display", "none"); window.addEventListener("deviceorientation", handleOrientationVR); } }).catch((error) => { console.log("请求授权报错。"); }) } else { //未触发重力感应 又没有授权 //判断 IOS12.2-13之间版本 toast("请在设置中打开动作与方向访问") } }

 

posted @ 2023-03-02 15:44  喵~喵  阅读(143)  评论(0编辑  收藏  举报