0
点赞
收藏
分享

微信扫一扫

JavaScript实现放大器

幸甚至哉歌以咏志 2022-03-26 阅读 96

demo介绍:

咱们今天做的放大器来源于京东(想看效果的直接去京东商城随便点开一个商品就可以看到)。为了方便,咱们直接把效果图进行了标记,在我们没有将鼠标移入app中,mask和big都是不可见的,当我们将鼠标移入app时,mask和big可见,且我们在app中移动鼠标时,mask对应的区域将在big中显示。(mask区域的宽高是相对固定的哈,就是app宽高的一半,本来想做一个鼠标动态划定mask大小的,但是我学识尚浅,还没办法完成)。

思路:

html部分我们只需要写一个id="app"的div,css部分咱啥都不用写,javascript部分,我们需要封装名为imageZoom的函数,我们在这个函数里面完成变量定义、变量获取、css样式设置、动态交互等一系列工作。mask和big都是咱们通过javascript中创造的div容器,这样有什么好处呢?按照常规思路,我们应该把mask、big跟app一起直接写在html中,也是毫无疑问可以实现相同效果的。但是我们通过javascript创造mask和big两个div容器,并且在javascript中设置了mask和big的样式,那以后我们要做图片放大器的时候,直接写一个类似于id="app"的容器,然后调用imageZoom()方法就可以了,啥mask啥big咱都不用管了,大大减少了我们的代码量啊,就非常方便。至于其他细节咱就不多说了,下面的代码中都有比较详细的注释,当然,也非常欢迎你直接问我。

实现代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div id="app"></div>

    <script src="/通用资源/publicPackage.js"></script>
    <script>
    var app = document.getElementById('app');
    imageZoom(app, "/img/jd.jpg", 175, 175);
    function imageZoom(dom, url, width, height) {
        var mask = document.createElement('div');
        var big = document.createElement('div');
        app.appendChild(mask);
        app.appendChild(big);
        css(app, {
            width: width + 'px',
            height: height + 'px',
            position: 'relative',
            backgroundImage: 'url('+ url +')',
            backgroundSize: 'cover',
            backgroundRepeat: 'no-repeat',
            border: '1px solid #ccc'
        });
        css(mask, {
            width: width / 2 + 'px',
            height: height / 2 + 'px',
            position: 'absolute',
            backgroundColor: 'rgba(214, 235, 99, 0.4)',
            cursor: 'move',
            display: 'none'
        });
        css(big, {
            width: width + 'px',
            height: height + 'px',
            position: 'absolute',
            top: 0,
            left: '100%',
            backgroundImage: 'url('+ url +')',
            backgroundRepeat: 'no-repeat',
            display: 'none'
        });
        //鼠标移入
        bindEvent(app, 'mouseenter', function(e) {
            //鼠标进入时位置
            //mask和big显示
            css(mask, {display: 'block'});
            css(big, {display: 'block'});
            //鼠标移动时位置
            bindEvent(dom, 'mousemove', function(e) {
                var mouse_newX = e.clientX;
                var mouse_newY = e.clientY;
                //mask左顶点位置
                var mask_left = mouse_newX - width / 4;
                var mask_top = mouse_newY - height /4;
                //边界处理(mask始终不能跑到app外头)
                if (mask_left < 0) {
                    mask_left = 0;
                } else if (mask_left > width / 2) {
                    mask_left = width / 2;
                };
                if(mask_top < 0) {
                    mask_top = 0;
                } else if (mask_top > height / 2) {
                    mask_top = height / 2;
                }
                //mask位置(mask随鼠标移动而移动)
                css(mask, {left: mask_left + 'px', top: mask_top + 'px'});
                //big背景图设置
                css(big, {
                    backgroundPositionX: '-' + mask_left * 2 + 'px',
                    backgroundPositionY: '-' + mask_top * 2 + 'px',
                    backgroundSize: '200%'
                })
            });
        });
        // 鼠标移出
        bindEvent(app, 'mouseleave', function(e) {
            css(mask, {display: 'none'});
            css(big, {display: 'none'});
        }); 
    }
    </script>
</body>
</html>

上述代码中是不是出现了一些没见过的方法,没见过就对了,因为是我自己日常封装的方法,比如这里的bindEvent()、css(),应该就用了这两个吧。其实这些自己封装的方法都可以通过我们学的属性、方法去代替,照样可以实现效果,封装一是为了兼顾ie浏览器,二当然是为了以后直接调用可以偷懒啦。这里双手奉上我封装的一些方法,也就是上面代码中引入的publicPackage.js文件哈,里面还有一些这里没有用到的方法,不管它就行,当然你也可以自己在其他地方使用。

/***
获取任何元素样式
    @element    元素
    @styleName  样式
***/ 
function getStyle(element, styleName) {
    if (window.getComputedStyle) {
        return getComputedStyle(element)[styleName];
    } else{
        var style= element.currentStyle;
        if (style) {
            styleName = styleName.replace(/-([a-z])?/g, function(match, $1) {
                return $1.toUpperCase();
            })
            return style[styleName];
        };
    };
};

/***
实现任何事件绑定(DOM0、DOM2、ie)
    @dom    元素
    @type   事件
    @fn     回调函数
***/ 
function bindEvent(dom, type, fn) {
    if (dom.addEventListener) {
        dom.addEventListener(type, fn);
    } else if (dom.attachEvent) {
        dom.attachEvent('on' + type, function(e) {
            e.target = e.srcElement;
            e.currentTarget = this;
            fn.call(dom, e);    //this指向
        });
    } else {
        var oldFun = dom['on' + type];
        dom['on' + type] = function(e) {
            oldFun && oldFun(e || window.event);
            fn(e || window.event);
        };
    };
};

/***
实现任何事件移除(DOM0、DOM2、ie)
    @dom    元素
    @type   事件
    @fn     回调函数
***/ 
function removeEvent(dom, type, fn) {
    if (dom.removeEventListener) {
        dom.removeEventListener(type, fn);
    } else if (dom.detachEvent) {
        dom.detachEvent('on' + type, fn);
    } else {
        dom['on' + type] = null;
    }
}

/***
封装防抖函数
    @fn     函数
***/ 
function throttle_event(fn) {
    clearTimeout(fn.__timebar);
    fn.__timebar = setTimeout(fn, 200);
};

/***
封装节流函数
    @fn     函数
***/ 
function throttle_time(fn) {
    //lock为true不执行
    if (fn.__lock) {
        return;
    };
    fn.__lock = true;
    fn();
    setTimeout(function() {
        fn.__lock = false;
    },1000);
};

/***
实现设置样式方法 css(dom, width, 200px) 或 css(dom, {width: '200px', color: 'red'})
    @dom    元素
    @key   样式名
    @value     样式值
***/ 
function css(dom, key, value) {
    if (typeof key === 'string') {
        dom.style[key] = value;
    } else {
        for (var name in key) {
            css(dom, name, key[name]);
        }
    }
}

效果展示:

放大器效果展示

踩坑:

1.鼠标定位,刚开始我使用的是offsetX和offsetY(鼠标位于元素内部的位置)。我绑定的是app这个容器,那么获取的就是鼠标到容器app的距离,而且相比clientX和clientY(鼠标在视口中的位置),我都不用去减去元素到document的距离,就可以轻松得到mask在app中的绝对位置。但是使用offsetX和offsetY后我发现,mask会随鼠标移动这没有问题,但是mask移动时一直闪烁,我换成clientX和clientY之后就没有闪烁的问题了,咋还对offsetX和offsetY有歧视呢,我百思不得其解。后来,老师说了原因,鼠标同时位于mask和app中时,它会返回到mask和到app的距离,这就混乱了,所以就会闪烁,根本上是因为产生了两个位置信息它不知道听谁的。

2.如果我们给app这个容器设置margin:300px 300px;(300只是一个比方,设置多少都行),我们就发现mask不听鼠标使唤了,咋回事呢?还是clientX和clientY的问题,clientX和clientY是到视口的距离,我们没有给app容器设置margin的时候,clientX减去app宽的1/4,clientY减去app高的1/4,得到mask左顶点的绝对位置,这是没问题的。但是当app容器有margin时,它并不是紧贴着document的,clientX减去app宽的1/4,clientY减去app高的1/4,得到的距离包含了margin,而且数值非常大已经超过边界了,所以mask只能死死地困在右下角。而对于这一问题,目前我还没找到合适的办法,如果直接减去margin,没有滚动页面的时候可以实现,但是一旦滚动页面又不行了(clientX和clientY在页面滚动时在视口中的位置会变)。

举报

相关推荐

0 条评论