JavaScript—— PC 端网页特效

Posted imByte

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaScript—— PC 端网页特效相关的知识,希望对你有一定的参考价值。

本篇为 javascript 系列笔记第八篇,将陆续更新后续内容。参考:黑马程序员JavaScript核心教程,前端基础教程

系列笔记:

JavaScript(一)—— 初识JavaScript / 注释 / 输入输出语句 / 变量 / 数据类型

JavaScript(二)—— 运算符 / 流程控制 / 数组

JavaScript(三)—— 函数 / 作用域 / 预解析 / 对象

JavaScript(四)—— 内置对象 / 简单数据类型与复杂类型

JavaScript(五)—— Web APIs 简介 / JavaScript 必须掌握的 DOM 操作

JavaScript(六)—— DOM 事件高级

JavaScript(七)—— BOM 浏览器对象模型
 

文章预览

 

「一」元素偏移量 offset


offset 系列相关属性可以 动态的 得到该元素的位置(偏移)、大小等:

  1. 获得元素距离带有定位父元素的位置
  2. 获得元素自身的大小(宽度、高度)
  3. 返回的数值不带单位

offset 系列常用属性如下:

  • offsetTopoffsetLeft 属性

  1. 父亲有定位,则以 父亲 为准,返回 45
  2. 父亲无定位,则以 body 为准,返回 195
  • offsetWidthoffsetHeight 属性
  1. 计算数值时包含 paddingborderwidth
  2. 可以动态获取数值,比如浏览器缩小时,数值相应变化
  • offsetParent 属性

  1. 返回带有定位的父亲,否则返回 body
  2. 与节点操作中 parentNode 相比,parentNode 返回最近一级的父亲,无论父亲是否有定位
  • offset 与 style 区别

offset

  1. offset 可以得到任意样式表中的样式值
  2. offset 系列获得的数值是没有单位的
  3. offsetWidth 包含 padding、border、width
  4. offsetWidth 等属性是只读属性,只能获取不能赋值
  5. 只想获取元素大小位置,offset 更合适

style

  1. style 只能得到行内样式表中的样式值
  2. style.width 获得的是带有单位的字符串
  3. style.width 获得不包含 padding 和 border 的值
  4. style.width 是可读写属性,可以获取也可以赋值
  5. 要想给元素更改值,需要用 style

  • 案例:获取鼠标在盒子内的坐标
var box = document.querySelector('.box');
box.addEventListener('mousemove', function (e) {
     var x = e.pageX - this.offsetLeft;
     var y = e.pageY - this.offsetTop;
     this.innerhtml = 'x坐标是' + x + ' y坐标是' + y;
 })
 box.addEventListener('mouseout', function () {
     this.innerHTML = '';
 })
  • 案例:模态框拖拽

var login = document.querySelector('.login');
var mask = document.querySelector('.login-bg');
var link = document.querySelector('#link');
var closeBtn = document.querySelector('#closeBtn');
var title = document.querySelector('#title');
// 显示
link.addEventListener('click', function () {
    mask.style.display = 'block';
    login.style.display = 'block';
})
// 隐藏
closeBtn.addEventListener('click', function () {
    mask.style.display = 'none';
    login.style.display = 'none';
})
// 拖拽
title.addEventListener('mousedown', function (e) {
    var x = e.pageX - login.offsetLeft;
    var y = e.pageY - login.offsetTop; 
    document.addEventListener('mousemove', move);
    function move(e) {
        login.style.left = e.pageX - x + 'px';
        login.style.top = e.pageY - y + 'px';
    }
    document.addEventListener('mouseup', function () {
        document.removeEventListener('mousemove', move);
    })
})
  1. 鼠标按下 mousedown,获取鼠标在盒子中坐标
  2. 鼠标移动 mousemove,求得模态框的 lefttop
  3. 鼠标弹起 mouseup,移除注册事件 removeEventListener
  • 案例:仿京动放大镜

部分 HTML 代码

<div class="preview_img">
    <img src="img/mac_small.jpg" alt="">	<--小图-->
    <div class="mask"></div>				<--遮罩-->
    <div class="preview_img_big">			<--大图-->
        <img src="img/mac_big.jpg" alt="" class="big_img">
    </div>
</div>

部分 CSS 代码

.preview_img {
    position: relative;
    height: 450px;
    border: 1px solid #ccc;
}

.mask {
    display: none;
    position: absolute;
    left: 0;
    top: 0;
    width: 300px;
    height: 300px;
    background: #FEDE4F;
    opacity: .5;        /* 不透明度 */
    border: 1px solid #ccc;
    cursor: move;       /* 鼠标样式为移动 */
}

.preview_img_big {
    display: none;
    position: absolute;
    left: 450px;
    top: -1px;
    width: 540px;
    height: 540px;
    border: 1px solid #ccc;
    overflow: hidden;
}

.big_img {
    position: absolute;
    top: 0;
    left: 0;
}

JS 代码

window.addEventListener('load', function () {
    var preview_img = document.querySelector('.preview_img');
    var mask = document.querySelector('.mask');
    var big = document.querySelector('.preview_img_big');

    preview_img.addEventListener('mouseover', function () {
        mask.style.display = 'block';
        big.style.display = 'block';
    })

    preview_img.addEventListener('mousemove', move);

    function move(e) {
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // 减去遮罩盒子高度一半 
        var maskX = x - mask.offsetWidth / 2;
        var maskY = y - mask.offsetHeight / 2;
        // 最大移动距离(宽高相等)
        var maskMax = preview_img.offsetWidth - mask.offsetWidth;
        if (maskX <= 0) {
            maskX = 0;
        } else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';
        // 获得大图片
        var big_img = document.querySelector('.big_img');
        // 最大移动距离
        var bigMax = big_img.offsetWidth - big.offsetWidth;
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        big_img.style.left = -bigX + 'px';
        big_img.style.top = -bigY + 'px';
    }

    preview_img.addEventListener('mouseout', function () {
        mask.style.display = 'none';
        big.style.display = 'none';
    })
})

注意:

  1. 若将 js 文件放在 html 上面,注意一定要先加载窗口 window.addEventListener('load', function(){}),否则注册事件绑定为空,出现报错
  2. 理解遮罩层和放大图片移动的算法
     

「二」元素可视区 client


通过 client 相关属性可以动态的得到该元素的边框大小、元素大小

  • client 系列和 offset 系列区别

     

「三」元素滚动 scroll


使用 scroll 相关属性可以动态得到该元素的大小、滚动距离等

  • onscroll 事件

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条,当滚动条向下滚动时,会触发 onscroll 事件

  • 案例:仿淘宝固定右侧侧边栏

部分 HTML 代码

JS 代码

var sliderbar = document.querySelector('.slider-bar');
var banner = document.querySelector('.banner');
 var bannerTop = banner.offsetTop;
 var sliderbarTop = sliderbar.offsetTop - bannerTop;

 var main = document.querySelector('.main');
 var goBack = document.querySelector('.goBack');
 var mainTop = main.offsetTop;

 document.addEventListener('scroll', function (e) {
     if (window.pageYOffset >= bannerTop) {
         sliderbar.style.position = 'fixed';
         sliderbar.style.top = sliderbarTop + 'px';
     } else {
         sliderbar.style.position = 'absolute';
         sliderbar.style.top = '300px';
     }
     if (window.pageYOffset >= mainTop) {
         goBack.style.display = 'block';
     } else {
         goBack.style.display = 'none';
     }
 })

注意:页面被卷去的头部,有兼容问题,通常有下面几种写法:

  1. 声明了 DTD(<!DOCTYPE html>),使用 document.documentElement.scrollTop
  2. 未声明 DTD,使用 document.body.scrollTop
  3. IE9 开始,新方法 window.pageYOffsetwindow.pageXOffset

 

「四」动画函数


  • 动画实现原理

通过定时器 setInterval() 不断移动盒子位置。实现步骤:

  1. 获得盒子当前位置
  2. 通过定时器不断重复移动单位距离(需要添加定位,使用 element.style.left )
  3. 添加结束定时条件

  • 动画函数的封装

可能一个页面中会多次调用动画过程,因此可以将其封装成函数。

注意:函数需要传递 2 个参数,动画对象移动的距离

function animate(obj, target) {
    var timer = setInterval(function () {
        if (obj.offsetLeft >= target) {
            clearInterval(timer);
        }
        obj.style.left = obj.offsetLeft + 2 + 'px';
    }, 10)
}
  • 动画函数给不同元素记录不同定时器

若每次调用都声明 var timer 变量,会造成占用大量内存以及重复命名歧义等问题。因此,这里利用给对象添加属性的方式进行赋值操作 obj.timer,实现了不同元素指定不同定时器

  1. 为避免持续点击导致开启多个定时器,应在函数调用开始时清除所有定时器
  2. 为避免停止后点击还会少量移动问题,在到达指定距离后,return 结束函数调用
  • 缓动效果原理

缓动动画就是让元素运动速度有所变化,最常见的是逐渐降速到停止,使得效果更加自然

算法: 步长 = (目标位置 - 当前位置)/ 10

 var step = (target - obj.offsetLeft) / 10;
 step = step > 0 ? Math.ceil(step) : Math.floor(step);


  1. 避免小数问题导致最后无法运动到指定 target, 这里利用了向上取整
  2. 此外,考虑到后退过程,向上取整也出现了问题。因此需要分类来讨论
  • 动画函数添加回调函数

回调函数原理:函数可以作为一个参数,作为传递到另一个函数中

function animate(obj, target, callback) {}	// callback 回调函数

animate(span, 500, function () {})


  • 动画函数封装到单独 JS 文件中

以后会经常使用动画函数,因此可以将其单独封装到一个 JS 文件中,使用时直接引用 JS 文件即可

  • 案例:滑动盒子
    <div class="sliderbar">
        <span></span>
        <div class="con">问题反馈</div>
    </div>

    <script>
        var sliderbar = document.querySelector('.sliderbar');
        var con = document.querySelector('.con');
        sliderbar.addEventListener('mouseenter', function () {
            animate(con, -160, function () {
                sliderbar.children[0].innerHTML = '→';
            });
        })
        sliderbar.addEventListener('mouseleave', function () {
            animate(con, 0, function () {
                sliderbar.children[0].innerHTML = '←';
            });
        })
    </script>
  1. mouseentermouseleavemouseovermouseout 用法一样,区别在于前者无法冒泡
  2. 注意要引用 animate.js
     

「五」常见网页特效案例


  • 案例:淘宝轮播图

JS 代码主要利用

  1. animate.js 代码
function animate(obj, target, callback) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function () {
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            clearInterval(obj.timer);
            callback && callback();
        }
        obj.style.left = obj.offsetLeft + step + 'px';
    }, 15);
}
  1. index.js 代码
window.addEventListener('load', function () {
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    var focusWidth = focus.offsetWidth;


    //  鼠标经过 focus 显示隐藏左右按钮
    focus.addEventListener('mouseenter', function () {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
        // 鼠标经过停止自动轮播
        clearInterval(timer);
        timer = null; // 清除定时器变量
    })
    focus.addEventListener('mouseleave', function () {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
        timer = setInterval(function () {
            //手动调用点击事件
            arrow_r.click();
        }, 2000);
    })


    // 动态生成小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    for (var i = 0; i < ul.children.length; i++) {
        var li = document.createElement('li');
        // 淘宝源码中 li 中又创建了 a,其实在这里不创建 a 也可以,本案例暂

以上是关于JavaScript—— PC 端网页特效的主要内容,如果未能解决你的问题,请参考以下文章

PC 端网页特效-offset 系列及学习案例

Javascript禁止父元素滚动条滚动, pc移动端均有效

PC 端网页特效

移动端PC端 网页特效

PC端访问WAP页面时自动跳转到PC版网页需要啥代码?

实现网页只能移动端访问PC不可访问