安卓端360度全景图的html5实现

这里是一款旅游相关的安卓应用,其中虚拟旅游的功能采用html5的360度全景图技术实现,使用户能够身临其境的感受旅游景点的风光。

此处引入了ddpanorama插件,它的原理是在canvas上绘制全景图,手指滑动时重绘canvas来实现。它包括通过手指滑动循环查看全景图,点击热点可进入另一全景图,缩放,离线访问等功能。

热点的计算和绘制:

可以在ddpanorama.js中看到在redraw这个方法也就是canvas重绘的时候定义了一个事件:

$(this.img).trigger(
                                jQuery.Event(ddpanoramas.event_prefix+"redraw", {
                                    scrollX : scrollX,
                                    canvas : this.canvas,
                                    speed : $(this.canvas).prop("speedX") / ddpanoramas.max_speed,
                                    loaded : loaded
                                }));
View Code

并且传出了一些参数,其中scrollX这个参数是针对原图比例的手指滑动时图片的偏移量,它在计算热点位置时起到至关重要的作用。因此在绑定ddredraw这个事件,在该事件触发时候进行热点的计算和绘制及点击事件的绑定。这里为了方便,我们还需要一个参数,就是针对canvas比例的手指滑动时图片的偏移量。这个在ddpanorama.js中setScrollX这个方法中的scrollX取得,我们可以定义一个全局变量canvasScrollX,将这个值存储起来,随时使用。

至于计算过程,需要慢慢调试,这里上个代码:

imgEle.ddpanorama({ width: bodyWidth, height: bodyHeight }).bind("ddredraw", function (event) {
        var canvas = event.canvas;
        var ctx = canvas.getContext("2d");
        var imgNatualWidth = imgEle.get()[0].naturalWidth;
        var imgNatualHeight = imgEle.get()[0].naturalHeight;
        var fontSize = imgNatualHeight / 40;
        if (hotPointInfo != null) {
            for (var i = 0; i < hotPointInfo.length; i++) {
                if (hotPointInfo[i] != null && hotPointInfo[i].PanImgHotType != null && hotPointInfo[i].PanImgInfoID != null && hotPointInfo[i].PanImgHotType == 0 && hotPointInfo[i].PanImgInfoID == GetHotPanImgInfoID) {
                    var hotPanImgInfoId = hotPointInfo[i].HotPanImgInfoID;
                    var scrollX = event.scrollX + hotPointInfo[i].HotPosX;
                    var canvasNatualWidth = bodyWidth * imgNatualHeight / bodyHeight; //原始图可见区域的宽
                    if (event.scrollX > 0) {
                        if (hotPointInfo[i].HotPosX > canvasNatualWidth) {
                            scrollX = event.scrollX - imgNatualWidth + hotPointInfo[i].HotPosX;
                        }
                    }
                    if (event.scrollX < -hotPointInfo[i].HotPosX - hotPointInfo[i].PanImgHotName.length * fontSize) {
                        scrollX = event.scrollX + hotPointInfo[i].HotPosX + imgNatualWidth;
                    }
                    ctx.beginPath();
                    ctx.globalAlpha = 0.6;
                    ctx.fillStyle = "black";
                    showRoundRect(ctx, scrollX - fontSize, hotPointInfo[i].HotPosY - fontSize * 4 / 3, (hotPointInfo[i].PanImgHotName.length + 2) * fontSize, fontSize * 2, fontSize / 2);
                    ctx.globalAlpha = 1;
                    showText(ctx, fontSize + "px 微软雅黑, Arial", "#fff", hotPointInfo[i].PanImgHotName, scrollX, hotPointInfo[i].HotPosY);
                    ctx.closePath();
                }
            }
        }
        $(canvas).unbind("click").click(function (e) {
            var mouseCoord = getMouseCoord(e);
            var isOnHotPoint = false;
            if (hotPointInfo != null) {
                for (var i = 0; i < hotPointInfo.length; i++) {
                    if (hotPointInfo[i].PanImgHotType == 0) {
                        var canvasX = hotPointInfo[i].HotPosX * bodyHeight / imgNatualHeight;
                        var canvasY = hotPointInfo[i].HotPosY * bodyHeight / imgNatualHeight;
                        var totalWidth = imgNatualWidth * bodyHeight / imgNatualHeight;
                        var canvasFontSize = fontSize * bodyHeight / imgNatualHeight;
                        if (canvasScrollX < bodyWidth - canvasX && canvasScrollX > -canvasX - hotPointInfo[i].PanImgHotName.length * canvasFontSize - 20) {
                            var isOntheArea = detectMousePos(mouseCoord, canvasScrollX + canvasX - canvasFontSize * 2, canvasY - canvasFontSize * 2, (hotPointInfo[i].PanImgHotName.length + 4) * canvasFontSize, canvasFontSize * 4);
                            if (isOntheArea == true) {
                                isOnHotPoint = true;
                            }
                            canvasClick(isOntheArea, hotPointInfo[i].HotPanImgInfoID);
                        } else if (canvasScrollX > -canvasX || canvasScrollX < bodyWidth - canvasX - totalWidth) {
                            var isOntheArea;
                            if (canvasX + canvasScrollX - totalWidth - canvasFontSize * 2 < 0) {
                                isOntheArea = detectMousePos(mouseCoord, canvasX + canvasScrollX + totalWidth - canvasFontSize * 2, canvasY - canvasFontSize * 2, (hotPointInfo[i].PanImgHotName.length + 4) * canvasFontSize, canvasFontSize * 4);
                            } else {
                                isOntheArea = detectMousePos(mouseCoord, canvasX + canvasScrollX - totalWidth - canvasFontSize * 2, canvasY - canvasFontSize * 2, (hotPointInfo[i].PanImgHotName.length + 4) * canvasFontSize, canvasFontSize * 4);
                            }
                            if (isOntheArea == true) {
                                isOnHotPoint = true;
                            }
                            canvasClick(isOntheArea, hotPointInfo[i].HotPanImgInfoID);
                        }
                    }
                }
            }
            //去除隐藏缩略图
            if (!isOnHotPoint) {
                //  showOrHide();
            }
        });
        try {
            isOnLine = Virtual.checkConnectNet();
        } catch (e) {
            hideloding();
        }
        if (isOnLine == 1) {
            localStorage.setItem("virtualTravelInfoObj" + sceAreaId, JSON.stringify(virtualTravelInfoObj));
        }
        hideloding();
    });
View Code

这里如何获取点击事件呢?

将整个canvas元素绑定click事件,然后通过click事件触发的坐标来计算,需要引入以下两个方法:

//获取鼠标事件的坐标
function getMouseCoord(e) {
    var mx = 0,
     my = 0;
    if (e.layerX || e.layerX == 0) {
        mx = e.layerX;
        my = e.layerY;
    } else if (e.offsetX || e.offsetX == 0) {
        mx = e.offsetX;
        my = e.offsetY;
    }
    var coord = { x: mx, y: my };
    return coord;
}
//检测鼠标是否在某个区域里
function detectMousePos(mouseCoord, x, y, w, h) {
    var isOntheArea = false;
    if (mouseCoord.x > x && mouseCoord.x < x + w && mouseCoord.y > y && mouseCoord.y < y + h) {
        isOntheArea = true;
    }
    return isOntheArea;
}
View Code

宽高自适应屏幕:

在插件初始化时候传入width,height可以设置canvas的宽高:

imgEle.ddpanorama({ width: bodyWidth, height: bodyHeight });

js获取屏幕宽:screen.availWidth,屏幕高:screen.availHeight

由于在移动端屏幕顶部有信号条,还有app壳占用了一些高度,所以更好的办法是由客户端开发人员通过url参数传webview的宽和高的值给我们调用。

离线访问:通过离线缓存和本地存储结合使用来实现。

设置一个全局对象来缓存数据(一般是JSON格式),每次ajax请求的数据都缓存在该对象中,待所有数据取得后,将该对象通过JSON.stringify方法转换成字符串格式,然后存到localStorage中。

通过navigator.onLine来判断网络状态(目前测过chrome支持),但在手机中会出现无效的情况,这时候客户端提供一个接口来判断网络状态,如果是onLine,那么ajax请求数据,如果是offLine,那么从localStoage中取数据,然后通过JSON.parse方法转换为JSON格式的对象。

对于嵌在APP中的网页来说,离线缓存和本地存储都需要客户端代码的支持,不然目前会出现无效的情况,这点要注意。

 

 

 

 

posted @ 2013-12-26 16:34  yayadoudou  阅读(1954)  评论(0编辑  收藏  举报