整理思路。
先搭结构。
<div class="slide-box">
<div class="cover">
<img src="./images/fengmian.jpg" alt="封面图" />
<span class="time">03:48span>
div>
<div class="progress">
<div class="bar">
<div class="all">
<div class="already">div>
div>
div>
div>
div>
根据DOM结构来写样式。
使用js代码来写幻灯片的js逻辑代码。
kabab-case
-> 连接符 slide-box
camelCase
-> 小驼峰 slideBox
PascalCase
-> 帕斯卡尔(大驼峰) SlideBox
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>幻灯片title>
<link rel="stylesheet" href="./css/reset.min.css" />
<style>
/* 禁止页面出现横向滚动条。 */
html,
body {
overflow-x: hidden;
}
/* 开始设置盒子 */
.slide-box {
position: relative;
box-sizing: border-box;
margin: 20px auto;
width: 200px;
height: 100px;
overflow: hidden;
}
/* 封面区域样式 */
.slide-box .cover {
position: relative;
height: 100%;
background: #eee;
transition: opacity 0.3s;
}
.slide-box .cover img {
display: block;
height: 100%;
width: 100%;
}
.slide-box .cover .time {
position: absolute;
right: 5px;
bottom: 5px;
z-index: 1;
padding: 5px 10px;
font-size: 12px;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
}
/* 进度区域样式 */
.slide-box .progress {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #eee;
opacity: 0;
z-index: -1;
transition: opacity 0.3s;
}
.slide-box:hover .progress {
opacity: 1;
z-index: 10;
}
.slide-box:hover .cover {
opacity: 0;
}
/* .slide-box .progress {
background: url("./images/225865760.png") no-repeat;
//真实开发中,背景图大小是不固定的。
//宽度:盒子宽 * 10 ; //10是协定好的,一般一行展示10个。不过也可以由服务器传递一个数字过来。
//高度:Math.ceil(总图片数 / 10) * 盒子高 ; //总图片数是后端返回的。
background-size: 2000px 550px;
background-position: 0 0;//默认展示背景图中的第一张,但是后期需要根据鼠标在盒子中的位置,计算出应该展示那一张,并计算出对应的background-position值。
} */
.slide-box .progress .bar {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
padding: 5px 10px;
width: 100%;
background: #000;
}
.slide-box .progress .bar .all {
position: relative;
height: 4px;
border-radius: 2px;
background: rgba(255, 255, 255, 0.3);
overflow: hidden;
}
.slide-box .progress .bar .already {
position: absolute;
top: 0;
bottom: 0;
height: 100%;
border-radius: 2px;
background: #fff;
width: 0%; /* 播放进度,默认为0,后期根据鼠标的移动,动态计算进度。 */
}
style>
head>
<body>
<div class="slide-box">
<div class="cover">
<img src="./images/fengmian.jpg" alt="封面图" />
<span class="time">03:48span>
div>
<div class="progress">
<div class="bar">
<div class="all">
<div class="already">div>
div>
div>
div>
div>
body>
html>
<script>
// 获取需要操作的元素。
const slideBox = document.querySelector(".slide-box");
const progressBox = slideBox.querySelector(".progress");
const alreadyBox = progressBox.querySelector(".already");
// 初始化数据和样式。
let W = slideBox.offsetWidth; //盒子宽度。
let H = slideBox.offsetHeight; //盒子高度。
let C = 10; //进度条图片中每一行展示的数量。
let T = 46; //进度条图片的总图片数量。
let URL = "./images/225865760.png"; //背景图地址,从服务器获取的是绝对地址。
progressBox.style.cssText = `
background: url(${URL}) no-repeat;
background-size: ${W * C}px ${Math.ceil(T / C) * H}px;
background-position: 0 0;
`;
alreadyBox.style.width = "0%";
//移动计算。
const computed = function computed(ev) {
// 前提是没有横向滚动条。
console.log(`slideBox-->`, slideBox);
let l = ev.clientX - slideBox.getBoundingClientRect().left; //鼠标距离盒子左侧的距离。
let ratio = l / W; //计算鼠标距离盒子左边的百分比。
ratio = ratio < 0 ? 0 : ratio > 1 ? 1 : ratio;
// 控制进度条的样式。
alreadyBox.style.width = `${ratio * 100}%`;
// 控制背景图的样式。
let N = Math.round(T * ratio);
// console.log(`N-->`, N);
N = N < 1 ? 1 : N > T ? T : N;//解决盒子开头移动时出现空白的情况。
let x = N % C; //计算在第几列。
x = x === 0 ? C : x;//解决在盒子中间移动时出现空白的情况。
let y = Math.ceil(N / C); //计算在第几行。
progressBox.style.backgroundPosition = `${-(x - 1) * W}px ${
-(y - 1) * H
}px`;
};
slideBox.addEventListener("mouseenter", computed); //初次进来盒子。
slideBox.addEventListener("mousemove", computed); //在盒子内移动。
script>
js插件封装的技巧
在封装插件的时候,我们一般都基于面向对象的方式来处理。
在相同的页面中,我们的插件可能会执行很多次。为了保证每一次调用插件,相互之间不影响,我们采用OOP面向对象模式。
插件本身是一个类,每一次使用都是创建这个类的一个实例,实例和实例之间是不影响的!对于一些通用的处理方法,实例和实例之间还可以共用!
对插件样式的处理:
DOCTYPE html>
<html>
<body>
<div class="zfslide-box" id="slide1">
<div class="zfslide-cover">
<img src="./images/fengmian.jpg" alt="封面图" />
div>
<div class="zfslide-progress">
<div class="zfslide-bar">
<div class="zfslide-all">
<div class="zfslide-already">div>
div>
div>
div>
div>
<div class="zfslide-box" id="slide2">
<div class="zfslide-cover">
<img src="./images/fengmian.jpg" alt="封面图" />
<span class="time">03:48span>
div>
<div class="zfslide-progress">div>
div>
body>
html>
<style>
/* 在插件样式的基础上,自己增加/调整新的样式。 */
#slide1,
#slide2 {
margin: 20px auto;
}
#slide2 {
width: 300px;
height: 165px;
}
style>
<style>
/* 对新增的元素自己设置样式 */
#slide2 .time {
position: absolute;
right: 5px;
bottom: 5px;
z-index: 1;
padding: 5px 10px;
font-size: 12px;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
}
style>
<style>
/* 修改插件内部元素的样式。 */
.zfslide-progress .zfslide-all{
background-color: rgba(255, 255, 255, 0.7);
}
style>
对插件逻辑的处理:
(function () {
// 检测是否为纯粹对象
const toString = Object.prototype.toString;
const isPlainObject = function isPlainObject(obj) {
// 先校验:如果基于 toString/call ,检测结果都不是 [object Object],则一定不是纯粹对象
if (toString.call(obj) !== "[object Object]") return false;
let proto = Object.getPrototypeOf(obj);
if (!proto) return true;
let Ctor = "constructor" in obj && obj.constructor;
return Ctor === Object;
};
class Slide {
constructor(container, { back, total, column, progress }) {
//获取需要的元素。
this.container = container;
this.progress = container.querySelector(".zfslide-progress");
if (!this.progress) {
throw new TypeError(`缺少幻灯片图层-样式式: zfslide-progress`);
}
// 动态创建进度条。
if (progress&&!container.querySelector(".zfslide-already")) {
let div = document.createElement("div");
div.className = "zfslide-bar";
div.innerHTML = ``;
this.progress.appendChild(div)
}
this.already = container.querySelector(".zfslide-already");
// 初始化样式和数据
this.W = container.offsetWidth; //盒子宽度。
this.H = container.offsetHeight; //盒子高度。
this.column = +column; //进度条图片中每一行展示的数量。
this.total = +total; //进度条图片的总图片数量。
this.back = back; //背景图地址,从服务器获取的是绝对地址。
this.init();
// 事件绑定。-需要在绑定先,把computed中的this绑定为当前实例。
container.addEventListener("mouseenter", this.computed.bind(this)); //初次进来盒子。
container.addEventListener("mousemove", this.computed.bind(this)); //在盒子内移动。
}
// 处理化样式。
init() {
let { progress, already, back, W, column, total, H } = this;
if (already) {
already.style.width = "0%";
}
progress.style.cssText = `
background: url(${back}) no-repeat;
background-size: ${W * column}px ${Math.ceil(total / column) * H}px;
background-position: 0 0;
`;
}
// 鼠标在盒子中移动的计算。
computed(ev) {
let { container, already, progress, W, H, total, column } = this;
// 前提是没有横向滚动条。
console.log(`container-->`, container);
// 计算鼠标距离盒子左边的百分比。
let l = ev.clientX - container.getBoundingClientRect().left; //鼠标距离盒子左侧的距离。
let ratio = l / W; //计算鼠标距离盒子左边的百分比。
ratio = ratio < 0 ? 0 : ratio > 1 ? 1 : ratio;
// 控制进度条的样式。
if (already) {
already.style.width = `${ratio * 100}%`;
}
// 计算出当前应该展示那一张。
let N = Math.round(total * ratio); //当前要显示的第几张。
// console.log(`N-->`, N);
N = N < 1 ? 1 : N > total ? total : N; //解决盒子开头移动时出现空白的情况。
// 计算出当前这一张图片在背景图中是第x列和第y行。
let y = Math.ceil(N / column); //计算在第几行。
let x = N % column; //计算在第几列。
x = x === 0 ? column : x; //解决在盒子中间移动时出现空白的情况。
//控制背景图的位置。
progress.style.backgroundPosition = `${-(x - 1) * W}px ${-(y - 1) * H}px`;
}
}
// 暴露API。
const zfslide = function zfslide(selector, options) {
//如果传递的是一个选择器:我们基于选择器获取相应的元素。
if (typeof selector === "string") {
selector = document.querySelector(selector);
}
// 确保容器的正确性
if (!selector || selector?.nodeType !== 1) {
throw new TypeError(`指定的容器不存在`);
}
//处理配置项。
if (!isPlainObject(options)) {
options = {};
}
options = Object.assign(
{
back: "", //背景图图片地址。
total: 0, //背景图片图片总数。
column: 10, //背景图每行数量有多少个。
progress: true,
},
options
);
// 确保背景图图片地址和背景图每行数量是存在的。
if (!options.back || options.total === 0) {
throw new TypeError(`请先指定幻灯片背景图和背景图单行数量`);
}
return new Slide(selector, options);
};
if (typeof window !== "undefined") {
window["zfslide"] = zfslide;
}
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = zfslide;
}
})();
在html中使用插件。
DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>测试幻灯片插件title>
<link rel="stylesheet" href="./css/image.slide.css" />
<link rel="stylesheet" href="./css/reset.min.css" />
<style>
/* 在插件样式的基础上,自己增加/调整新的样式。 */
#slide1,
#slide2 {
margin: 20px auto;
}
#slide2 {
width: 300px;
height: 165px;
}
/* 对新增的元素自己设置样式 */
#slide2 .time {
position: absolute;
right: 5px;
bottom: 5px;
z-index: 1;
padding: 5px 10px;
font-size: 12px;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
}
/* 修改插件内部元素的样式。 */
.zfslide-progress .zfslide-all {
background-color: rgba(255, 255, 0, 0.4);
}
style>
head>
<body>
<div class="zfslide-box" id="slide1">
<div class="zfslide-cover">
<img src="./images/fengmian.jpg" alt="封面图" />
div>
<div class="zfslide-progress">
<div class="zfslide-bar">
<div class="zfslide-all">
<div class="zfslide-already">div>
div>
div>
div>
div>
<div class="zfslide-box" id="slide2">
<div class="zfslide-cover">
<img src="./images/fengmian.jpg" alt="封面图" />
<span class="time">03:48span>
div>
<div class="zfslide-progress">div>
div>
<div class="zfslide-box" id="slide3">
<div class="zfslide-cover">
<img src="./images/fengmian.jpg" alt="封面图" />
<span class="time">03:48span>
div>
<div class="zfslide-progress">div>
div>
body>
html>
<script src="./js/zfslide.plugin.js">script>
<script>
zfslide("#slide1", {
back: "./images/225865760.png",
total: 46,
});
zfslide("#slide2", {
back: "./images/3x3.jpg",
total: 9,
column: 3,
progress: false,
});
zfslide("#slide3", {
back: "./images/3x3.jpg",
total: 9,
column: 3,
});
script>
NativeApp与WebApp
随着科技的发展,H5占据一款App的比例越来越高,Native比例在快速的降低,直到有的App,Native只需要搭建一个壳子,内部全部都是H5来写的…
后来出现了一些前端框架,可以帮助我们快速构建出一个App应用的壳子。
再后来经过逐步的完善,出现了移动端App开发的前端框架:
目前开发App已经是过去式了。开发出来,也不容易推广。目前主流的模式是小程序!
方案一:jsBridge-主流模式,适用于IOS和安卓。
在H5页面中,导入微信写好的jsBridge文件。
基于wx.config(…)执行一些初始的配置。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名
jsApiList: [] // 必填,需要使用的JS接口列表
});
基于wx对象上的方法,调用微信提供的api。
wx.ready(function () { //需在用户可能点击分享按钮前就先调用
wx.updateAppMessageShareData({
title: '', // 分享标题
desc: '', // 分享描述
link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '', // 分享图标
success: function () {
// 设置成功
}
})
});
方案二:URL劫持-适用于IOS。
原理:因为H5是运行在NativeApp中的,所以H5中的任何操作,原生App都可以知道,例如页面跳转。
location.href=`wx://xxx.cn/xxx`
//其中:`wx://`是伪协议,是我们和原生商量好的,只要我跳转这样的地址,原生就进行拦截!
//基于拦截,原生就知道了我们要请求的功能和传递的参数,然后帮我们调用其所实现的某个方法即可!