首页
友链
统计
留言
更多
直播
壁纸
推荐
我的毛线
哔哔点啥
Search
1
pxe 自动化安装系统
660 阅读
2
本站官方群:894703859------|诚邀各位大佬的入驻!
649 阅读
3
软件安装
471 阅读
4
新款螺旋帽子编织#夏凉帽#合股线夏凉帽编织
405 阅读
5
10 个Linux Awk文本处理经典案例
376 阅读
linux
yaml
iptables
shell
ansible
ssl
awk
sed
pxe
prometheus
Nginx
k8s
fish
dev
go占位符
clickhouse
html标签
vue基础
html表格
vue项目
vscode
css基础
css定位
css精灵图
code
html5
project
js
jQuery
面向对象
编织
编织视频
常用工具
微软
登录
/
注册
Search
标签搜索
基础
js
Nginx
css
webapi
jQuery
面向对象
command
项目
ansible
用户权限
go
html
文件管理
命令
k8s
shell
pxe
awk
vscode
JustDoIt
累计撰写
114
篇文章
累计收到
4
条评论
首页
栏目
linux
yaml
iptables
shell
ansible
ssl
awk
sed
pxe
prometheus
Nginx
k8s
fish
dev
go占位符
clickhouse
html标签
vue基础
html表格
vue项目
vscode
css基础
css定位
css精灵图
code
html5
project
js
jQuery
面向对象
编织
编织视频
常用工具
微软
页面
友链
统计
留言
直播
壁纸
推荐
我的毛线
哔哔点啥
搜索到
114
篇与
的结果
2022-07-10
正则表达式概述
1.1什么是正则表达式正则表达式( Regular Expression )是用于匹配字符串中字符组合的模式。在JavaScript中,正则表达式也是对象。正则表通常被用来检索、替换那些符合某个模式(规则)的文本,例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)。此外,正则表达式还常用于过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等 。其他语言也会使用正则表达式,本阶段我们主要是利用JavaScript 正则表达式完成表单验证。1.2 正则表达式的特点 灵活性、逻辑性和功能性非常的强。 可以迅速地用极简单的方式达到字符串的复杂控制。 对于刚接触的人来说,比较晦涩难懂。比如:^\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*$ 实际开发,一般都是直接复制写好的正则表达式. 但是要求会使用正则表达式并且根据实际情况修改正则表达式. 比如用户名: /^[a-z0-9_-]{3,16}$/ 2.正则表达式在js中的使用2.1正则表达式的创建在 JavaScript 中,可以通过两种方式创建一个正则表达式。方式一:通过调用RegExp对象的构造函数创建var regexp = new RegExp(/123/); console.log(regexp); 方式二:利用字面量创建 正则表达式 var rg = /123/; 2.2测试正则表达式test() 正则对象方法,用于检测字符串是否符合该规则,该对象会返回 true 或 false,其参数是测试字符串。var rg = /123/; console.log(rg.test(123));//匹配字符中是否出现123 出现结果为true console.log(rg.test('abc'));//匹配字符中是否出现123 未出现结果为false 3.正则表达式中的特殊字符3.1正则表达式的组成一个正则表达式可以由简单的字符构成,比如 /abc/,也可以是简单和特殊字符的组合,比如 /ab*c/ 。其中特殊字符也被称为元字符,在正则表达式中是具有特殊意义的专用符号,如 ^ 、$ 、+ 等。特殊字符非常多,可以参考:MDNjQuery 手册:正则表达式部分[正则测试工具]( <http://tool.oschina.net/regex)3.2边界符正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符 边界符 说明 ^ 表示匹配行首的文本(以谁开始) $ 表示匹配行尾的文本(以谁结束) 如果 ^和 $ 在一起,表示必须是精确匹配。var rg = /abc/; // 正则表达式里面不需要加引号 不管是数字型还是字符串型 // /abc/ 只要包含有abc这个字符串返回的都是true console.log(rg.test('abc')); console.log(rg.test('abcd')); console.log(rg.test('aabcd')); console.log('---------------------------'); var reg = /^abc/; console.log(reg.test('abc')); // true console.log(reg.test('abcd')); // true console.log(reg.test('aabcd')); // false console.log('---------------------------'); var reg1 = /^abc$/; // 精确匹配 要求必须是 abc字符串才符合规范 console.log(reg1.test('abc')); // true console.log(reg1.test('abcd')); // false console.log(reg1.test('aabcd')); // false console.log(reg1.test('abcabc')); // false 3.3字符类字符类表示有一系列字符可供选择,只要匹配其中一个就可以了。所有可供选择的字符都放在方括号内。3.3.1 [] 方括号表示有一系列字符可供选择,只要匹配其中一个就可以了var rg = /[abc]/; // 只要包含有a 或者 包含有b 或者包含有c 都返回为true console.log(rg.test('andy'));//true console.log(rg.test('baby'));//true console.log(rg.test('color'));//true console.log(rg.test('red'));//false var rg1 = /^[abc]$/; // 三选一 只有是a 或者是 b 或者是c 这三个字母才返回 true console.log(rg1.test('aa'));//false console.log(rg1.test('a'));//true console.log(rg1.test('b'));//true console.log(rg1.test('c'));//true console.log(rg1.test('abc'));//true ---------------------------------------------------------------------------------- var reg = /^[a-z]$/ //26个英文字母任何一个字母返回 true - 表示的是a 到z 的范围 console.log(reg.test('a'));//true console.log(reg.test('z'));//true console.log(reg.test('A'));//false ----------------------------------------------------------------------------------- //字符组合 var reg1 = /^[a-zA-Z0-9]$/; // 26个英文字母(大写和小写都可以)任何一个字母返回 true ------------------------------------------------------------------------------------ //取反 方括号内部加上 ^ 表示取反,只要包含方括号内的字符,都返回 false 。 var reg2 = /^[^a-zA-Z0-9]$/; console.log(reg2.test('a'));//false console.log(reg2.test('B'));//false console.log(reg2.test(8));//false console.log(reg2.test('!'));//true 3.3.2量词符量词符用来设定某个模式出现的次数。 量词 说明 * 重复0次或更多次 + 重复1次或更多次 ? 重复0次或1次 {n} 重复n次 {n,} 重复n次或更多次 {n,m} 重复n到m次 3.3.3用户名表单验证功能需求: 如果用户名输入合法, 则后面提示信息为: 用户名合法,并且颜色为绿色 如果用户名输入不合法, 则后面提示信息为: 用户名不符合规范, 并且颜色为红色 分析: 用户名只能为英文字母,数字,下划线或者短横线组成, 并且用户名长度为6~16位. 首先准备好这种正则表达式模式/$[a-zA-Z0-9-_]{6,16}^/ 当表单失去焦点就开始验证. 如果符合正则规范, 则让后面的span标签添加 right类. 如果不符合正则规范, 则让后面的span标签添加 wrong类. <input type="text" class="uname"> <span>请输入用户名</span> <script> // 量词是设定某个模式出现的次数 var reg = /^[a-zA-Z0-9_-]{6,16}$/; // 这个模式用户只能输入英文字母 数字 下划线 中划线 var uname = document.querySelector('.uname'); var span = document.querySelector('span'); uname.onblur = function() { if (reg.test(this.value)) { console.log('正确的'); span.className = 'right'; span.innerHTML = '用户名格式输入正确'; } else { console.log('错误的'); span.className = 'wrong'; span.innerHTML = '用户名格式输入不正确'; } } </script> 3.3.4 括号总结1.大括号 量词符. 里面表示重复次数2.中括号 字符集合。匹配方括号中的任意字符.3.小括号表示优先级正则表达式在线测试3.4预定义类预定义类指的是某些常见模式的简写方式.案例:验证座机号码var reg = /^\d{3}-\d{8}|\d{4}-\d{7}$/; var reg = /^\d{3,4}-\d{7,8}$/; 表单验证案例//手机号验证:/^1[3|4|5|7|8][0-9]{9}$/; //验证通过与不通过更换元素的类名与元素中的内容 if (reg.test(this.value)) { // console.log('正确的'); this.nextElementSibling.className = 'success'; this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您输入正确'; } else { // console.log('不正确'); this.nextElementSibling.className = 'error'; this.nextElementSibling.innerHTML = '<i class="error_icon"></i>格式不正确,请从新输入 '; } //QQ号验证: /^[1-9]\d{4,}$/; //昵称验证:/^[\u4e00-\u9fa5]{2,8}$/ //验证通过与不通过更换元素的类名与元素中的内容 ,将上一步的匹配代码进行封装,多次调用即可 function regexp(ele, reg) { ele.onblur = function() { if (reg.test(this.value)) { // console.log('正确的'); this.nextElementSibling.className = 'success'; this.nextElementSibling.innerHTML = '<i class="success_icon"></i> 恭喜您输入正确'; } else { // console.log('不正确'); this.nextElementSibling.className = 'error'; this.nextElementSibling.innerHTML = '<i class="error_icon"></i> 格式不正确,请从新输入 '; } } }; //密码验证:/^[a-zA-Z0-9_-]{6,16}$/ //再次输入密码只需匹配与上次输入的密码值 是否一致 3.5正则替换replacereplace() 方法可以实现替换字符串操作,用来替换的参数可以是一个字符串或是一个正则表达式。var str = 'andy和red'; var newStr = str.replace('andy', 'baby'); console.log(newStr)//baby和red //等同于 此处的andy可以写在正则表达式内 var newStr2 = str.replace(/andy/, 'baby'); console.log(newStr2)//baby和red //全部替换 var str = 'abcabc' var nStr = str.replace(/a/,'哈哈') console.log(nStr) //哈哈bcabc //全部替换g var nStr = str.replace(/a/a,'哈哈') console.log(nStr) //哈哈bc哈哈bc //忽略大小写i var str = 'aAbcAba'; var newStr = str.replace(/a/gi,'哈哈')//"哈哈哈哈bc哈哈b哈哈" 案例:过滤敏感词汇<textarea name="" id="message"></textarea> <button>提交</button> <div></div> <script> var text = document.querySelector('textarea'); var btn = document.querySelector('button'); var div = document.querySelector('div'); btn.onclick = function() { div.innerHTML = text.value.replace(/激情|gay/g, '**'); } </script>
2022年07月10日
109 阅读
0 评论
0 点赞
2022-07-10
函数的定义和调用
1.1函数的定义方式 方式1 函数声明方式 function 关键字 (命名函数)function fn(){} 方式2 函数表达式(匿名函数)var fn = function(){} 方式3 new Function()var f = new Function('a', 'b', 'console.log(a + b)'); f(1, 2); var fn = new Function('参数1','参数2'..., '函数体') 注意 /*Function 里面参数都必须是字符串格式 第三种方式执行效率低,也不方便书写,因此较少使用 所有函数都是 Function 的实例(对象) 函数也属于对象 */ 1.2函数的调用/* 1. 普通函数 */ function fn() { console.log('人生的巅峰'); } fn(); /* 2. 对象的方法 */ var o = { sayHi: function() { console.log('人生的巅峰'); } } o.sayHi(); /* 3. 构造函数*/ function Star() {}; new Star(); /* 4. 绑定事件函数*/ btn.onclick = function() {}; // 点击了按钮就可以调用这个函数 /* 5. 定时器函数*/ setInterval(function() {}, 1000); 这个函数是定时器自动1秒钟调用一次 /* 6. 立即执行函数(自调用函数)*/ (function() { console.log('人生的巅峰'); })(); 2.this2.1函数内部的this指向这些 this 的指向,是当我们调用函数的时候确定的。调用方式的不同决定了this 的指向不同一般指向我们的调用者.2.2改变函数内部 this 指向2.2.1 call方法call()方法调用一个对象。简单理解为调用函数的方式,但是它可以改变函数的 this 指向应用场景: 经常做继承.var o = { name: 'andy' } function fn(a, b) { console.log(this); console.log(a+b) }; fn(1,2)// 此时的this指向的是window 运行结果为3 fn.call(o,1,2)//此时的this指向的是对象o,参数使用逗号隔开,运行结果为3 以上代码运行结果为:2.2.2 apply方法apply() 方法调用一个函数。简单理解为调用函数的方式,但是它可以改变函数的 this 指向。应用场景: 经常跟数组有关系var o = { name: 'andy' } function fn(a, b) { console.log(this); console.log(a+b) }; fn()// 此时的this指向的是window 运行结果为3 fn.apply(o,[1,2])//此时的this指向的是对象o,参数使用数组传递 运行结果为3 2.2.3 bind方法bind() 方法不会调用函数,但是能改变函数内部this 指向,返回的是原函数改变this之后产生的新函数如果只是想改变 this 指向,并且不想调用这个函数的时候,可以使用bind应用场景:不调用函数,但是还想改变this指向 var o = { name: 'andy' }; function fn(a, b) { console.log(this); console.log(a + b); }; var f = fn.bind(o, 1, 2); //此处的f是bind返回的新函数 f();//调用新函数 this指向的是对象o 参数使用逗号隔开 2.2.4 call、apply、bind三者的异同 共同点 : 都可以改变this指向 不同点: call 和 apply 会调用函数, 并且改变函数内部this指向. call 和 apply传递的参数不一样,call传递参数使用逗号隔开,apply使用数组传递 bind 不会调用函数, 可以改变函数内部this指向. 应用场景 call 经常做继承. apply经常跟数组有关系. 比如借助于数学对象实现数组最大值最小值 bind 不调用函数,但是还想改变this指向. 比如改变定时器内部的this指向. 3.严格模式3.1什么是严格模式JavaScript 除了提供正常模式外,还提供了严格模式(strict mode)。ES5 的严格模式是采用具有限制性 JavaScript变体的一种方式,即在严格的条件下运行 JS 代码。严格模式在 IE10 以上版本的浏览器中才会被支持,旧版本浏览器中会被忽略。严格模式对正常的 JavaScript 语义做了一些更改:1.消除了 Javascript 语法的一些不合理、不严谨之处,减少了一些怪异行为。2.消除代码运行的一些不安全之处,保证代码运行的安全。3.提高编译器效率,增加运行速度。4.禁用了在 ECMAScript 的未来版本中可能会定义的一些语法,为未来新版本的 Javascript 做好铺垫。比如一些保留字如:class,enum,export, extends, import, super 不能做变量名3.2开启严格模式严格模式可以应用到整个脚本或个别函数中。因此在使用时,我们可以将严格模式分为为脚本开启严格模式和为函数开启严格模式两种情况。 情况一 :为脚本开启严格模式 有的 script 脚本是严格模式,有的 script 脚本是正常模式,这样不利于文件合并,所以可以将整个脚本文件放在一个立即执行的匿名函数之中。这样独立创建一个作用域而不影响其他script 脚本文件。(function (){ //在当前的这个自调用函数中有开启严格模式,当前函数之外还是普通模式 "use strict"; var num = 10; function fn() {} })(); //或者 <script> "use strict"; //当前script标签开启了严格模式 </script> <script> //当前script标签未开启严格模式 </script> 情况二: 为函数开启严格模式 要给某个函数开启严格模式,需要把“use strict”; (或 'use strict'; ) 声明放在函数体所有语句之前。function fn(){ "use strict"; return "123"; } //当前fn函数开启了严格模式 3.3严格模式中的变化严格模式对 Javascript 的语法和行为,都做了一些改变。'use strict' num = 10 console.log(num)//严格模式后使用未声明的变量 -------------------------------------------------------------------------------- var num2 = 1; delete num2;//严格模式不允许删除变量 -------------------------------------------------------------------------------- function fn() { console.log(this); // 严格模式下全局作用域中函数中的 this 是 undefined } fn(); --------------------------------------------------------------------------------- function Star() { this.sex = '男'; } // Star();严格模式下,如果 构造函数不加new调用, this 指向的是undefined 如果给他赋值则 会报错. var ldh = new Star(); console.log(ldh.sex); ---------------------------------------------------------------------------------- setTimeout(function() { console.log(this); //严格模式下,定时器 this 还是指向 window }, 2000); 更多严格模式要求参考4.高阶函数高阶函数是对其他函数进行操作的函数,它接收函数作为参数或将函数作为返回值输出。此时fn 就是一个高阶函数函数也是一种数据类型,同样可以作为参数,传递给另外一个参数使用。最典型的就是作为回调函数。同理函数也可以作为返回值传递回来5.闭包5.1变量的作用域复习变量根据作用域的不同分为两种:全局变量和局部变量。 函数内部可以使用全局变量。 函数外部不可以使用局部变量。 当函数执行完毕,本作用域内的局部变量会销毁。 5.2什么是闭包闭包(closure)指有权访问另一个函数作用域中变量的函数。简单理解就是 ,一个作用域可以访问另外一个函数内部的局部变量。5.3闭包的作用作用:延伸变量的作用范围。 function fn() { var num = 10; function fun() { console.log(num); } return fun; } var f = fn(); f(); 5.4闭包的案例 利用闭包的方式得到当前li 的索引号 for (var i = 0; i < lis.length; i++) { // 利用for循环创建了4个立即执行函数 // 立即执行函数也成为小闭包因为立即执行函数里面的任何一个函数都可以使用它的i这变量 (function(i) { lis[i].onclick = function() { console.log(i); } })(i); } 闭包应用-3秒钟之后,打印所有li元素的内容 for (var i = 0; i < lis.length; i++) { (function(i) { setTimeout(function() { console.log(lis[i].innerHTML); }, 3000) })(i); } 闭包应用-计算打车价格 /*需求分析 打车起步价13(3公里内), 之后每多一公里增加 5块钱. 用户输入公里数就可以计算打车价格 如果有拥堵情况,总价格多收取10块钱拥堵费*/ var car = (function() { var start = 13; // 起步价 局部变量 var total = 0; // 总价 局部变量 return { // 正常的总价 price: function(n) { if (n <= 3) { total = start; } else { total = start + (n - 3) * 5 } return total; }, // 拥堵之后的费用 yd: function(flag) { return flag ? total + 10 : total; } } })(); console.log(car.price(5)); // 23 console.log(car.yd(true)); // 33 5.5案例 var name = "The Window"; var object = { name: "My Object", getNameFunc: function() { return function() { return this.name; }; } }; console.log(object.getNameFunc()()) ----------------------------------------------------------------------------------- var name = "The Window"; var object = { name: "My Object", getNameFunc: function() { var that = this; return function() { return that.name; }; } }; console.log(object.getNameFunc()()) 6.递归6.1什么是递归**递归:**如果一个函数在内部可以调用其本身,那么这个函数就是递归函数。简单理解:函数内部自己调用自己, 这个函数就是递归函数**注意:**递归函数的作用和循环效果一样,由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件return。6.2利用递归求1~n的阶乘//利用递归函数求1~n的阶乘 1 * 2 * 3 * 4 * ..n function fn(n) { if (n == 1) { //结束条件 return 1; } return n * fn(n - 1); } console.log(fn(3)); 6.3利用递归求斐波那契数列// 利用递归函数求斐波那契数列(兔子序列) 1、1、2、3、5、8、13、21... // 用户输入一个数字 n 就可以求出 这个数字对应的兔子序列值 // 我们只需要知道用户输入的n 的前面两项(n-1 n-2)就可以计算出n 对应的序列值 function fb(n) { if (n === 1 || n === 2) { return 1; } return fb(n - 1) + fb(n - 2); } console.log(fb(3)); 6.4利用递归遍历数据// 我们想要做输入id号,就可以返回的数据对象 var data = [{ id: 1, name: '家电', goods: [{ id: 11, gname: '冰箱', goods: [{ id: 111, gname: '海尔' }, { id: 112, gname: '美的' }, ] }, { id: 12, gname: '洗衣机' }] }, { id: 2, name: '服饰' }]; //1.利用 forEach 去遍历里面的每一个对象 function getID(json, id) { var o = {}; json.forEach(function(item) { // console.log(item); // 2个数组元素 if (item.id == id) { // console.log(item); o = item; return o; // 2. 我们想要得里层的数据 11 12 可以利用递归函数 // 里面应该有goods这个数组并且数组的长度不为 0 } else if (item.goods && item.goods.length > 0) { o = getID(item.goods, id); } }); return o; }
2022年07月10日
115 阅读
0 评论
0 点赞
2022-07-03
构造函数和原型
1.1对象的三种创建方式--复习 字面量方式var obj = {}; new关键字var obj = new Object(); 构造函数方式function Person(name,age){ this.name = name; this.age = age; } var obj = new Person('zs',12); 1.2静态成员和实例成员1.2.1实例成员实例成员就是构造函数内部通过this添加的成员 如下列代码中uname age sing 就是实例成员,实例成员只能通过实例化的对象来访问 function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('我会唱歌'); } } var ldh = new Star('刘德华', 18); console.log(ldh.uname);//实例成员只能通过实例化的对象来访问 1.2.2静态成员静态成员 在构造函数本身上添加的成员 如下列代码中 sex 就是静态成员,静态成员只能通过构造函数来访问 function Star(uname, age) { this.uname = uname; this.age = age; this.sing = function() { console.log('我会唱歌'); } } Star.sex = '男'; var ldh = new Star('刘德华', 18); console.log(Star.sex);//静态成员只能通过构造函数来访问 1.3构造函数的问题构造函数方法很好用,但是存在浪费内存的问题。1.4构造函数原型prototype构造函数通过原型分配的函数是所有对象所共享的。JavaScript 规定,每一个构造函数都有一个prototype 属性,指向另一个对象。注意这个prototype就是一个对象,这个对象的所有属性和方法,都会被构造函数所拥有。我们可以把那些不变的方法,直接定义在 prototype 对象上,这样所有对象的实例就可以共享这些方法。function Star(uname, age) { this.uname = uname; this.age = age; } Star.prototype.sing = function() { console.log('我会唱歌'); } var ldh = new Star('刘德华', 18); var zxy = new Star('张学友', 19); ldh.sing();//我会唱歌 zxy.sing();//我会唱歌 1.5对象原型对象都会有一个属性 __proto__ 指向构造函数的 prototype 原型对象,之所以我们对象可以使用构造函数 prototype 原型对象的属性和方法,就是因为对象有 __proto__ 原型的存在。 __proto__对象原型和原型对象 prototype 是等价的 __proto__对象原型的意义就在于为对象的查找机制提供一个方向,或者说一条路线,但是它是一个非标准属性,因此实际开发中,不可以使用这个属性,它只是内部指向原型对象 prototype 1.6constructor构造函数对象原型( __proto__)和构造函数(prototype)原型对象里面都有一个属性 constructor 属性 ,constructor 我们称为构造函数,因为它指回构造函数本身。 constructor 主要用于记录该对象引用于哪个构造函数,它可以让原型对象重新指向原来的构造函数。 一般情况下,对象的方法都在构造函数的原型对象中设置。如果有多个对象的方法,我们可以给原型对象采取对象形式赋值,但是这样就会覆盖构造函数原型对象原来的内容,这样修改后的原型对象 constructor 就不再指向当前构造函数了。此时,我们可以在修改后的原型对象中,添加一个 constructor 指向原来的构造函数。 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数如: function Star(uname, age) { this.uname = uname; this.age = age; } // 很多情况下,我们需要手动的利用constructor 这个属性指回 原来的构造函数 Star.prototype = { // 如果我们修改了原来的原型对象,给原型对象赋值的是一个对象,则必须手动的利用constructor指回原来的构造函数 constructor: Star, // 手动设置指回原来的构造函数 sing: function() { console.log('我会唱歌'); }, movie: function() { console.log('我会演电影'); } } var zxy = new Star('张学友', 19); console.log(zxy) 以上代码运行结果,设置constructor属性如图:如果未设置constructor属性,如图:1.7原型链 每一个实例对象又有一个__proto__属性,指向的构造函数的原型对象,构造函数的原型对象也是一个对象,也有__proto__属性,这样一层一层往上找就形成了原型链。1.8构造函数实例和原型对象三角关系1.构造函数的prototype属性指向了构造函数原型对象 2.实例对象是由构造函数创建的,实例对象的__proto__属性指向了构造函数的原型对象 3.构造函数的原型对象的constructor属性指向了构造函数,实例对象的原型的constructor属性也指向了构造函数 1.9原型链和成员的查找机制任何对象都有原型对象,也就是prototype属性,任何原型对象也是一个对象,该对象就有__proto__属性,这样一层一层往上找,就形成了一条链,我们称此为原型链;当访问一个对象的属性(包括方法)时,首先查找这个对象自身有没有该属性。 如果没有就查找它的原型(也就是 __proto__指向的 prototype 原型对象)。 如果还没有就查找原型对象的原型(Object的原型对象)。 依此类推一直找到 Object 为止(null)。 __proto__对象原型的意义就在于为对象成员查找机制提供一个方向,或者说一条路线。 1.10原型对象中this指向构造函数中的this和原型对象的this,都指向我们new出来的实例对象function Star(uname, age) { this.uname = uname; this.age = age; } var that; Star.prototype.sing = function() { console.log('我会唱歌'); that = this; } var ldh = new Star('刘德华', 18); // 1. 在构造函数中,里面this指向的是对象实例 ldh console.log(that === ldh);//true // 2.原型对象函数里面的this 指向的是 实例对象 ldh 1.11通过原型为数组扩展内置方法 Array.prototype.sum = function() { var sum = 0; for (var i = 0; i < this.length; i++) { sum += this[i]; } return sum; }; //此时数组对象中已经存在sum()方法了 可以始终 数组.sum()进行数据的求 2.继承2.1call() call()可以调用函数 call()可以修改this的指向,使用call()的时候 参数一是修改后的this指向,参数2,参数3..使用逗号隔开连接 function fn(x, y) { console.log(this); console.log(x + y); } var o = { name: 'andy' }; fn.call(o, 1, 2);//调用了函数此时的this指向了对象o, 2.2子构造函数继承父构造函数中的属性 先定义一个父构造函数 再定义一个子构造函数 子构造函数继承父构造函数的属性(使用call方法) // 1. 父构造函数 function Father(uname, age) { // this 指向父构造函数的对象实例 this.uname = uname; this.age = age; } // 2 .子构造函数 function Son(uname, age, score) { // this 指向子构造函数的对象实例 3.使用call方式实现子继承父的属性 Father.call(this, uname, age); this.score = score; } var son = new Son('刘德华', 18, 100); console.log(son); 2.3借用原型对象继承方法 先定义一个父构造函数 再定义一个子构造函数 子构造函数继承父构造函数的属性(使用call方法) // 1. 父构造函数 function Father(uname, age) { // this 指向父构造函数的对象实例 this.uname = uname; this.age = age; } Father.prototype.money = function() { console.log(100000); }; // 2 .子构造函数 function Son(uname, age, score) { // this 指向子构造函数的对象实例 Father.call(this, uname, age); this.score = score; } // Son.prototype = Father.prototype; 这样直接赋值会有问题,如果修改了子原型对象,父原型对象也会跟着一起变化 Son.prototype = new Father(); // 如果利用对象的形式修改了原型对象,别忘了利用constructor 指回原来的构造函数 Son.prototype.constructor = Son; // 这个是子构造函数专门的方法 Son.prototype.exam = function() { console.log('孩子要考试'); } var son = new Son('刘德华', 18, 100); console.log(son); 如上代码结果如图:3.ES5新增方法3.1数组方法forEach遍历数组 arr.forEach(function(value, index, array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 }) //相当于数组遍历的 for循环 没有返回值 3.2数组方法filter过滤数组 var arr = [12, 66, 4, 88, 3, 7]; var newArr = arr.filter(function(value, index,array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 return value >= 20; }); console.log(newArr);//[66,88] //返回值是一个新数组 3.3数组方法somesome 查找数组中是否有满足条件的元素 var arr = [10, 30, 4]; var flag = arr.some(function(value,index,array) { //参数一是:数组元素 //参数二是:数组元素的索引 //参数三是:当前的数组 return value < 3; }); console.log(flag);//false返回值是布尔值,只要查找到满足条件的一个元素就立马终止循环 3.4筛选商品案例 定义数组对象数据var data = [{ id: 1, pname: '小米', price: 3999 }, { id: 2, pname: 'oppo', price: 999 }, { id: 3, pname: '荣耀', price: 1299 }, { id: 4, pname: '华为', price: 1999 }, ]; 使用forEach遍历数据并渲染到页面中data.forEach(function(value) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>'; tbody.appendChild(tr); }); 根据价格筛选数据 获取到搜索按钮并为其绑定点击事件search_price.addEventListener('click', function() { }); 使用filter将用户输入的价格信息筛选出来search_price.addEventListener('click', function() { var newDate = data.filter(function(value) { //start.value是开始区间 //end.value是结束的区间 return value.price >= start.value && value.price <= end.value; }); console.log(newDate); }); 将筛选出来的数据重新渲染到表格中 将渲染数据的逻辑封装到一个函数中function setDate(mydata) { // 先清空原来tbody 里面的数据 tbody.innerHTML = ''; mydata.forEach(function(value) { var tr = document.createElement('tr'); tr.innerHTML = '<td>' + value.id + '</td><td>' + value.pname + '</td><td>' + value.price + '</td>'; tbody.appendChild(tr); }); } 将筛选之后的数据重新渲染 search_price.addEventListener('click', function() { var newDate = data.filter(function(value) { return value.price >= start.value && value.price <= end.value; }); console.log(newDate); // 把筛选完之后的对象渲染到页面中 setDate(newDate); }); 根据商品名称筛选 获取用户输入的商品名称 为查询按钮绑定点击事件,将输入的商品名称与这个数据进行筛选 search_pro.addEventListener('click', function() { var arr = []; data.some(function(value) { if (value.pname === product.value) { // console.log(value); arr.push(value); return true; // return 后面必须写true } }); // 把拿到的数据渲染到页面中 setDate(arr); }) 3.5some和forEach区别 如果查询数组中唯一的元素, 用some方法更合适,在some 里面 遇到 return true 就是终止遍历 迭代效率更高 在forEach 里面 return 不会终止迭代 3.6trim方法去除字符串两端的空格var str = ' hello ' console.log(str.trim()) //hello 去除两端空格 var str1 = ' he l l o ' console.log(str.trim()) //he l l o 去除两端空格 3.7获取对象的属性名Object.keys(对象) 获取到当前对象中的属性名 ,返回值是一个数组 var obj = { id: 1, pname: '小米', price: 1999, num: 2000 }; var result = Object.keys(obj) console.log(result)//[id,pname,price,num] 3.8Object.definePropertyObject.defineProperty设置或修改对象中的属性Object.defineProperty(对象,修改或新增的属性名,{ value:修改或新增的属性的值, writable:true/false,//如果值为false 不允许修改这个属性值 enumerable: false,//enumerable 如果值为false 则不允许遍历 configurable: false //configurable 如果为false 则不允许删除这个属性 属性是否可以被删除或是否可以再次修改特性 })
2022年07月03日
92 阅读
0 评论
0 点赞
2022-07-02
JavaScript高级基础
1.面向过程与面向对象1.1面向过程 面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候再一个一个的依次调用就可以了。 1.2面向对象 面向对象是把事务分解成为一个个对象,然后由对象之间分工与合作。 1.3面向过程与面向对象对比 面向过程 面向对象 优点 性能比面向对象高,适合跟硬件联系很紧密的东西,例如单片机就采用的面向过程编程。 易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统 更加灵活、更加易于维护 缺点 不易维护、不易复用、不易扩展 性能比面向过程低 2.对象与类2.1对象对象是由属性和方法组成的:是一个无序键值对的集合,指的是一个具体的事物 属性:事物的特征,在对象中用属性来表示(常用名词) 方法:事物的行为,在对象中用方法来表示(常用动词) 2.1.1创建对象//以下代码是对对象的复习 //字面量创建对象 var ldh = { name: '刘德华', age: 18 } console.log(ldh); //构造函数创建对象 function Star(name, age) { this.name = name; this.age = age; } var ldh = new Star('刘德华', 18)//实例化对象 console.log(ldh); 如上两行代码运行结果为:2.2类 在 ES6 中新增加了类的概念,可以使用 class 关键字声明一个类,之后以这个类来实例化对象。类抽象了对象的公共部分,它泛指某一大类(class)对象特指某一个,通过类实例化一个具体的对象 2.2.1创建类 语法: //步骤1 使用class关键字 class name { // class body } //步骤2使用定义的类创建实例 注意new关键字 var xx = new name(); 示例 // 1. 创建类 class 创建一个 明星类 class Star { // 类的共有属性放到 constructor 里面 constructor(name, age) { this.name = name; this.age = age; } } // 2. 利用类创建对象 new var ldh = new Star('刘德华', 18); console.log(ldh); 以上代码运行结果:通过结果我们可以看出,运行结果和使用构造函数方式一样2.2.2类创建添加属性和方法 // 1. 创建类 class 创建一个类 class Star { // 类的共有属性放到 constructor 里面 constructor是 构造器或者构造函数 constructor(uname, age) { this.uname = uname; this.age = age; }//------------------------------------------->注意,方法与方法之间不需要添加逗号 sing(song) { console.log(this.uname + '唱' + song); } } // 2. 利用类创建对象 new var ldh = new Star('刘德华', 18); console.log(ldh); // Star {uname: "刘德华", age: 18} ldh.sing('冰雨'); // 刘德华唱冰雨 以上代码运行结果:注意哟: 通过class 关键字创建类, 类名我们还是习惯性定义首字母大写 类里面有个constructor 函数,可以接受传递过来的参数,同时返回实例对象 constructor 函数 只要 new 生成实例时,就会自动调用这个函数, 如果我们不写这个函数,类也会自动生成这个函数 多个函数方法之间不需要添加逗号分隔 生成实例 new 不能省略 语法规范, 创建类 类名后面不要加小括号,生成实例 类名后面加小括号, 构造函数不需要加function 2.2.3类的继承 语法 // 父类 class Father{ } // 子类继承父类 class Son extends Father { } 示例 class Father { constructor(surname) { this.surname= surname; } say() { console.log('你的姓是' + this.surname); } } class Son extends Father{ // 这样子类就继承了父类的属性和方法 } var damao= new Son('刘'); damao.say(); //结果为 你的姓是刘 以上代码运行结果: 子类使用super关键字访问父类的方法//定义了父类 class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } //子元素继承父类 class Son extends Father { constructor(x, y) { super(x, y); //使用super调用了父类中的构造函数 } } var son = new Son(1, 2); son.sum(); //结果为3 注意: 继承中,如果实例化子类输出一个方法,先看子类有没有这个方法,如果有就先执行子类的 继承中,如果子类里面没有,就去查找父类有没有这个方法,如果有,就执行父类的这个方法(就近原则) 如果子类想要继承父类的方法,同时在自己内部扩展自己的方法,利用super 调用父类的构造函数,super 必须在子类this之前调用 // 父类有加法方法 class Father { constructor(x, y) { this.x = x; this.y = y; } sum() { console.log(this.x + this.y); } } // 子类继承父类加法方法 同时 扩展减法方法 class Son extends Father { constructor(x, y) { // 利用super 调用父类的构造函数 super 必须在子类this之前调用,放到this之后会报错 super(x, y); this.x = x; this.y = y; } subtract() { console.log(this.x - this.y); } } var son = new Son(5, 3); son.subtract(); //2 son.sum();//8 以上代码运行结果为: 时刻注意this的指向问题,类里面的共有的属性和方法一定要加this使用. constructor中的this指向的是new出来的实例对象 自定义的方法,一般也指向的new出来的实例对象 绑定事件之后this指向的就是触发事件的事件源 在 ES6 中类没有变量提升,所以必须先定义类,才能通过类实例化对象 3.面向对象版tab 栏切换3.1功能需求 点击 tab栏,可以切换效果. 点击 + 号, 可以添加 tab 项和内容项. 点击 x 号, 可以删除当前的tab项和内容项. 双击tab项文字或者内容项文字可以修改里面的文字内容 3.2案例准备 获取到标题元素 获取到内容元素 获取到删除的小按钮 x号 新建js文件,定义类,添加需要的属性方法(切换,删除,增加,修改) 时刻注意this的指向问题 3.3切换 为获取到的标题绑定点击事件,展示对应的内容区域,存储对应的索引 this.lis[i].index = i; this.lis[i].onclick = this.toggleTab; 使用排他,实现只有一个元素的显示 toggleTab() { //将所有的标题与内容类样式全部移除 for (var i = 0; i < this.lis.length; i++) { this.lis[i].className = ''; this.sections[i].className = ''; } //为当前的标题添加激活样式 this.className = 'liactive'; //为当前的内容添加激活样式 that.sections[this.index].className = 'conactive'; } 3.4添加 为添加按钮+ 绑定点击事件 this.add.onclick = this.addTab; 实现标题与内容的添加,做好排他处理addTab() { that.clearClass(); // (1) 创建li元素和section元素 var random = Math.random(); var li = '<li class="liactive"><span>新选项卡</span><span class="iconfont icon-guanbi"> </span></li>'; var section = '<section class="conactive">测试 ' + random + '</section>'; // (2) 把这两个元素追加到对应的父元素里面 that.ul.insertAdjacentHTML('beforeend', li); that.fsection.insertAdjacentHTML('beforeend', section); that.init(); } 3.5删除 为元素的删除按钮x绑定点击事件 this.remove[i].onclick = this.removeTab; 获取到点击的删除按钮的所在的父元素的所有,删除对应的标题与内容 removeTab(e) { e.stopPropagation(); // 阻止冒泡 防止触发li 的切换点击事件 var index = this.parentNode.index; console.log(index); // 根据索引号删除对应的li 和section remove()方法可以直接删除指定的元素 that.lis[index].remove(); that.sections[index].remove(); that.init(); // 当我们删除的不是选中状态的li 的时候,原来的选中状态li保持不变 if (document.querySelector('.liactive')) return; // 当我们删除了选中状态的这个li 的时候, 让它的前一个li 处于选定状态 index--; // 手动调用我们的点击事件 不需要鼠标触发 that.lis[index] && that.lis[index].click(); } 3.6编辑 为元素(标题与内容)绑定双击事件 this.spans[i].ondblclick = this.editTab; this.sections[i].ondblclick = this.editTab; 在双击事件处理文本选中状态,修改内部DOM节点,实现新旧value值的传递editTab() { var str = this.innerHTML; // 双击禁止选定文字 window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); // alert(11); this.innerHTML = '<input type="text" />'; var input = this.children[0]; input.value = str; input.select(); // 文本框里面的文字处于选定状态 // 当我们离开文本框就把文本框里面的值给span input.onblur = function() { this.parentNode.innerHTML = this.value; }; // 按下回车也可以把文本框里面的值给span input.onkeyup = function(e) { if (e.keyCode === 13) { // 手动调用表单失去焦点事件 不需要鼠标离开操作 this.blur(); } } }
2022年07月02日
122 阅读
0 评论
1 点赞
2022-06-07
最详细的 Go学习笔记总结
第1章 Go介绍package main import "fmt" func main() { fmt.Println("hello word") } 第2章 Go基本语法2.1变量2.1.1. go语言中变量分为局部变量和全局变量 局部变量,是定义在打括号{}内部的变量,打括号内部也是局部变量的作用域 全局变量,是定义在函数和打括号外部{}的变量 2.1.2. 变量声明格式: var 变量名 变量类型 批量声明未初始化的变量var { a int b string c []float32 e struct { x int y string } } 初始化变量var a int = 20 #标准声明格式 var b = 30 #自动推断类型格式 c := 40 #初始化声明格式,首选 2.1.3.变量多重赋值以简单算法交换为例,传统写法如下var a int = 10 var b int = 20 b,a = a,b 2.1.4.匿名变量Go语言中的函数可以返回多个值,而事实上并不是所有返回值都用的上,那么就可以用匿名变量 “_” 替换即可,匿名变量不占用命名空间,也不会分配内存func GetData()(int,int){ return 10,20 } a,_ := GetData() //舍弃第二个返回值 _,b = GetData()//舍弃第一个返回值 2.2 数据类型2.3 打印格式化2.4 数据类型转换Go语言采用数据类型前置加括号的方式进行类型转换,格式如:T(表达式)。T表示要转换的类型a := 10 b := string(a) //将int型转换为string型 c := float32(a) //将int型转换为float型 2.5 常量相对于变量,常量是不变的值。 常量是一个简单的标识符,在程序运行时,不会被修改格式如下: const 标识符 [类型] = 值 const PAI string = "abc" 2.5.1 常量用于枚举 const ( USERNAME = "geinihua" PASSWORD = "geinihua" NETWORK = "tcp" SERVER = "10.247.22.146" PORT = "3306" DATABASE = "demo" ) dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s",USERNAME,PASSWORD,NETWORK,SERVER,PORT,DATABASE) 常量组中如果不指定类型和初始值,则与上一行非空常量值相同const ( a=10 b c ) fmt.PrintLn(a,b,c) //输出结果10 10 10 2.5.2 iota枚举 iota常量自动生成器,每隔一行,自动加1 iota给常量赋值使用 iota遇到下个const,会重置为0 多个常量可以写一个iota,在一个括号里 多重赋值,在同一行,值一样 3. 流程控制 3.1 if 条件判断语句 func max(num1, num2 int) int { /* 声明局部变量 */ var result int if num1 > num2 { result = num1 } else { result = num2 } return result } 3.2 switch 条件选择语句 grade := "" score := 88.5 switch true { case score >=90: grade = "A" case score >=80: grade = "B" case score >=70: grade = "C" default: grade="E" } fmt.Printf("你的登记是: %s\n",grade ) 3.3 for 循环语句 第一种写法: for i:=0;i<=20 ;i++ { fmt.Printf("%d\n",i) } 第二种写法: var i int for i<=20 { fmt.Printf("%d\n",i) } 第三种写法(for ...range): str := "123ABCabc好" for i,value := range str{ fmt.Printf("第 %d 位的ASCII值=%d,字符是%c\n",i,value,value) } 4.Go语言的函数与指针4.1 函数func(参数列表)(返回参数列表){ //函数体 } 4.1.3 函数变量函数变量是把函数作为值保存到变量中.在Golang中,,函数也是一种类型,可以和其他类型一样被保存在变量中type myFunc func(int) bool func main(){ nums:=[]int{10,20,40,16,17,3030,49849,204394,43943,2923,23923,} fmt.Println(filter(nums,isEven)) fmt.Println(filter(nums,isAdd)) } func filter(arr []int, f myFunc) []int { var result []int for _, value := range arr { if f(value) { result = append(result, value) } } return result } func isEven(num int) bool{ if num%2 == 0 { return true }else { return false } } func isAdd(num int) bool{ if num%2 == 0 { return false } return true } 4.1.4 匿名函数匿名函数没有函数名,只有函数体,可以作为一种类型赋值给变量。匿名函数经常被用于实现回调函数、闭包等1.在定义匿名函数的时候就可以直接使用 res1 := func (n1 int, n2 int) int { return n1 + n2 }(10, 30) //括号里的10,30 就相当于参数列表,分别对应n1和n2 fmt.Println("res1=",res1) 2.将匿名函数赋给一个变量 res1 := func (n1 int, n2 int) int { return n1 + n2 } res2 := res1(50,50) fmt.Println("res1=",res2) 3.匿名函数作为回调函数 func vist(list []float64,f func(float64)) { for _,value:=range list{ f(value) } } List := []float64{1,2,5,20,90} vist(List, func(v float64) { sqrt := math.Pow(v,2) fmt.Println(sqrt) }) 4.1.5 闭包//函数f返回了一个函数,返回的这个函数就是一个闭包。这个函数本身中没有定义变量I的,而是引用了它所在的环境(函数f)中的变量i. func f(i int) func() int { return func() int{ i++ return i } } a:=f(0) fmt.Println(a()) //0 fmt.Println(a()) //1 fmt.Println(a()) //2 fmt.Println(a()) //3 4.1.6 可变参数语法格式: func 函数名(参数名...类型)(返回值列表){} 该语法格式定义了一个接受任何数目、任何类型参数的函数。这里特殊语法是三个点"...",在一个变量后面加上三个点,表示从该处开始接受可变参数func Tsum(nums ...int) { fmt.Println(nums) total:=0 for _,val := range nums{ total+=val } fmt.Println( total) } 4.1.7 golang单元测试要开始一个单元测试,需要准备一个 go 源码文件,在命名文件时需要让文件必须以_test结尾单元测试源码文件可以由多个测试用例组成,每个测试用例函数需要以Test为前缀,例如: 格式如下: func TestXXX( t *testing.T ) func sum2(n1 int, args ...int) int { sum := n1 for i := 0; i < len(args); i++ { sum += args[i] } return sum } func TestAvaiableSum(t *testing.T) { res := sum2(1, 23, 34, 56) fmt.Println("res=", res) } 4.2指针指针式存储另一个变量的内存地址的变量。变量是一种使用方便的占位符。一个指针变量可以指向任何一个值的内存地址 在Go语言中使用地址符&来获取变量的地址,一个变量前使用&会返回该变量的内存地址total:=20 fmt.Println("total的内存地址",&total) 4.2.1 声明指针格式:var 指针变量 *指针类型 声明指针,*T是指针变量的类型,它指向T类型的值,*号用于指定变量是一个指针var ip *int //指向整型的指针 var fp *float32 //指向浮点型的指针 指针使用流程1.定义指针变量 2.为指针变量赋值 3.访问指针变量中指向地址的值 获取指针变量指向的变量值:在指针类型的变量前加上号。如atype Student struct { name string age int sex int8 } func TestZhiz(t *testing.T) { s1:=Student{"steven",32,2} s2:=Student{"Sunny",10,1} var a *Student=&s1 //&s1的内存地址 var b *Student=&s2 //&s2的内存地址 fmt.Printf("s1类型为%T,值为%v\n",s1,s1) fmt.Printf("s2类型为%T,值为%v\n",s2,s2) fmt.Printf("a类型为%T,值为%v\n",a,a) fmt.Printf("b类型为%T,值为%v\n",b,b) fmt.Printf("s1的值等于a指针\n") fmt.Printf("s2的值等于b指针\n") fmt.Printf("*a类型为%T,值为%v\n",*a,*a) fmt.Printf("*b类型为%T,值为%v\n",*b,*b) } 空指针 if(ptr != nil) //ptr不是空指针 if(ptr == nil)//ptr是空指针 4.2.2 使用指针1.通过指针修改变量的值//指针修改变量的值 a2:=32 b2:=&a2 fmt.Println("a2的值",a2) //a2的值 32 fmt.Println("b2地址",b2) //b2地址 0xc4200142d8 fmt.Println("b2的值",*b2) //b2的值 32 *b2++ fmt.Println("b2的值",*b2) //b2的值 33 2.使用指针作为函数的参数将基本数据类型的指针作为函数的参数,可以实现对传入数据的修改,这是因为指针作为函数的参数只是赋值了一个指针,指针指向的内存没有发生改变func main(){ orgi:=68 ptr:=&orgi change(ptr) fmt.Println("执行函数后orgi的值",orgi) //执行函数后orgi的值 20 } func change(p *int) { *p=20 } 4.2.3 指针数组//指针数组 //格式:var ptr [3]*string ptrArr:=[COUNT]string{"abc","ABC","123","8888"} i:=0 //定义指针数组 var ptrPoint [COUNT]*string fmt.Printf("%T,%v \n",ptrPoint,ptrPoint) //[4]*string,[<nil> <nil> <nil> <nil>] //将数组中的每个元素地址赋值给指针数组 for i=0;i<COUNT;i++ { ptrPoint[i] = &ptrArr[i] } fmt.Printf("%T,%v \n",ptrPoint,ptrPoint) //[4]*string,[0xc42000e800 0xc42000e810 0xc42000e820 0xc42000e830] //循环取指针数组中的值 for i=0;i<COUNT;i++ { fmt.Printf("a[%d]=%v \n",i, *ptrPoint[i]) //a[0]=abc //a[1]=ABC //a[2]=123 //a[3]=8888 } 4.2.4 指针的指针指向指针的指针变量声明格式如下:var ptr **int//使用两个*号 //指针的指针 var a2 int var ptr2 *int var pptr **int a2=1234 ptr2=&a2 fmt.Println("ptr地址",ptr2) pptr=&ptr fmt.Println("pptr地址",pptr) fmt.Printf("变量a2=%d\n",a2) fmt.Printf("指针变量ptr2=%d\n",*ptr2) fmt.Printf("指向指针的指针量pptr=%d\n",**pptr) //输出结果 /* ptr地址 0xc4200d4140 pptr地址 0xc4200ec000 变量a2=1234 指针变量ptr2=1234 指向指针的指针量pptr=20 */ 4.3 函数的参数传递4.3.1 值传递(传值)值传递是指在调用函数时将实际参数复制一份传递到函数中,不会影响原内容数据 4.3.2 引用传递(传引用)1.引用传递是在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改将影响原内容数据 2.Go中可以借助指针来实现引用传递。函数参数使用指针参数,传参的时候其实是复制一份指针参数,也就是复制了一份变量地址 3.函数的参数如果是指针,当调用函数时,虽然参数是按复制传递的,但此时仅仅只是复制一个指针,也就是一个内存地址,这样不会造成内存浪费、时间开销 函数传int类型的值与引用对比package main import "fmt" func main() { //函数传int类型的值与引用对比 a:=200 fmt.Printf("变量a的内存地址%p,值为:%v\n",&a,a) changeIntVal(a) fmt.Printf("changeIntVal函数调用后变量a的内存地址%p,值为:%v\n",&a,a) changeIntPtr(&a) fmt.Printf("changeIntPtr函数调用后变量a的内存地址%p,值为:%v\n",&a,a) /* 变量a的内存地址0xc420080008,值为:200 changeIntVal函数,传递的参数n的内存地址:0xc420080018,值为:200 changeIntVal函数调用后变量a的内存地址0xc420080008,值为:200 changeIntPtr函数,传递的参数n的内存地址:0xc42008a020,值为:0xc420080008 changeIntPtr函数调用后变量a的内存地址0xc420080008,值为:50 */ } func changeIntVal(n int) { fmt.Printf("changeIntVal函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) n=90 } func changeIntPtr(n *int) { fmt.Printf("changeIntPtr函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) *n=50 } 函数传slice类型的值与引用对比import "fmt" func main() { //函数传slice类型的值与引用对比 a:=[]int{1,2,3,4} fmt.Printf("变量a的内存地址%p,值为:%v\n",&a,a) changeSliceVal(a) fmt.Printf("changeSliceVal函数调用后变量a的内存地址%p,值为:%v\n",&a,a) changeSlicePtr(&a) fmt.Printf("changeSlicePtr函数调用后变量a的内存地址%p,值为:%v\n",&a,a) } func changeSliceVal(n []int) { fmt.Printf("changeSliceVal函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) n[0]=90 } func changeSlicePtr(n *[]int) { fmt.Printf("changeSlicePtr函数,传递的参数n的内存地址:%p,值为:%v\n",&n,n) (*n)[1]=50 } 5.3 map 5.3.1 map概念 Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。 Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的 Map是hash表的一个引用,类型写为:map[key]value,其中的key, value分别对应一种数据类型,如:map[string]string 要求所有的key的数据类型相同,所有value数据类型相同(注:key与value可以有不同的数据类型) 5.3.2 map语法 声明map第一种方法 mapVar := map[key类型]value类型 第二种方法 mapVar := make(map[key类型]value类型) map初始化和遍历 mapVar:=map[string]string{ "a":"t1", "b":"t2", "c":"t3", } //遍历map for key, value := range mapVar { fmt.Printf("key=%v value=%v\n",key,value) } //查看元素在集合中是否存在 if value,ok:=mapVar["aa"];ok { fmt.Println("存在value",value) }else { fmt.Println("不存在value") } 5.3.3 map是引用类型 第6章 Go常用内置包可参考官网 字符串遍历 str:="strings包:遍历带有中文的字符串" for _, value := range []rune(str) { fmt.Printf("%c\n",value) } json序列化和反序列化 map序列化//第1种map声明方式 M2:= map[int]string{ 2:"aa", 3:"bb", } //第2种map声明方式 M:=make(map[int]string) M[1]="aaa" M[2]="bbb" res2,error:=json.Marshal(M2) res,error:=json.Marshal(M) if error!=nil { fmt.Printf("解析错误") } fmt.Printf(string(res2)) fmt.Printf(string(res)) //返回结果 //{"2":"aa","3":"bb"}{"1":"aaa","2":"bbb"} 结构体序列化package main import ( "encoding/json" "fmt" ) type Stu struct { Name string `json:"name"` Age int HIgh bool sex string Class *Class `json:"class"` } type Class struct { Name string Grade int } func main() { //实例化一个数据结构,用于生成json字符串 cla := new(Class) cla.Name = "1班" cla.Grade = 3 stu := Stu{ "张三", 18, true, "男", cla,//指针变量 } //Marshal失败时err!=nil jsonStu, err := json.Marshal(stu) if err != nil { fmt.Println("生成json字符串错误") } //jsonStu是[]byte类型,转化成string类型便于查看 fmt.Println(string(jsonStu)) 从结果中可以看出 只要是可导出成员(变量首字母大写),都可以转成json。因成员变量sex是不可导出的,故无法转成json。 如果变量打上了json标签,如Name旁边的 json:"name" ,那么转化成的json key就用该标签“name”,否则取变量名作为key,如“Age”,“HIgh”。 bool类型也是可以直接转换为json的value值。Channel, complex 以及函数不能被编码json字符串。当然,循环的数据结构也不行,它会导致marshal陷入死循环。 指针变量,编码时自动转换为它所指向的值,如cla变量。 (当然,不传指针,Stu struct的成员Class如果换成Class struct类型,效果也是一模一样的。只不过指针更快,且能节省内存空间。) //反序列化操作Unmarshal() var per_data Stu err2 := json.Unmarshal([]byte(jsonStu),&per_data) if err2 != nil { fmt.Printf("反序列化错误:%v\n", err) } fmt.Printf("personal_json反序列化=%v\n", per_data) fmt.Printf("per_data=%v\n", *per_data.Class) } 第7 章Go面向对象结构体 匿名结构体 和结构体匿名字段 匿名结构体就是没有名字的结构体,无须通过type关键字定义就可以直接使用。创建匿名结构体的时候,同时也要创建结构体对象 //匿名结构体 addr:=struct{ name string age int }{"slaiven",39} fmt.Println(addr) 匿名字段就是在结构体中的字段没有名字,只包含一个没有字段名的类型 如果字段没有名字,那么默认使用类型作为字段名,同一类型只能有一个匿名字段//匿名字段 user:=new(User) user.string="apce" user.int=84 fmt.Printf("名字%v,年龄%v",user.string,user.int) //名字apce,年龄84 结构体嵌套 将一个结构当作另一结构体的字段(属性),这种就是结构体嵌套,可以模拟以下两种关系. 聚合关系:一个类作为另一个类的属性,一定要采用有名字的结构体作为字段 继承关系:一个类作为另一个类的子类。子类与父类的关系。采用匿名字段的形式,匿名字段就该结构体的父类//聚合关系:一个类作为另一个类的属性 type Address struct { province,city string } type Person struct { name string age int address *Address } func TestMoudelStrings(t *testing.T) { //实例化Address结构体 addr:=Address{} addr.province="北京市" addr.city="丰台区" //实例化Person结构体 p:=Person{} p.name="Strven" p.age=28 p.address=&addr fmt.Println("姓名:",p.name,"年龄:",p.age,"省:",p.address.province,"市:",p.address.city) //如果修改了Person对象的address数据,那么对Address对象会有影响么?肯定的 p.address.city="大兴区" fmt.Println("姓名:",p.name,"年龄:",p.age,"省:",p.address.province,"市:",addr.city) //修改Address对象,是否会影响Persion对象数据?肯定的 addr.city="朝阳区" fmt.Println("姓名:",p.name,"年龄:",p.age,"省:",p.address.province,"市:",addr.city) } //继承关系:一个类作为另一个类的子类。子类与父类的关系 type Address struct { province,city string } type Person struct { name string age int Address //匿名字段,Address是Person的父类 } func TestMoudelStrings(t *testing.T) { //实例化Address结构体 addr:=Address{} addr.province="北京" addr.city="丰台区" //实例化Person结构体 p:=Person{"strven",38,addr} fmt.Printf("姓名:%v 年龄:%v 省:%v 市:%v\n",p.name,p.age,p.Address.province,p.Address.city) //姓名:strven 年龄:38 省:北京 市:丰台区 } 方法 Go中同时有函数和方法,方法的本质是函数,但是与函数又不同 1.含义不同,函数是一段具有独立功能的代码,可以被反复多次调用,而方法是一个类的行为功能,只有该类的对象才能调用 2.方法有接受者而函数没有,Go语言的方法是一种作用域特定类型变量的函数,这种类型变量叫作接受者(receiver),接受者的概念类似于传统面向对象中的this或self关键字 3.方法可以重名(接受者不同),而函数不能重名,type Per struct { name string age int } func ( p Per ) getData() { fmt.Printf("名字:%v 年龄:%v",p.name,p.age) //名字:aaa 年龄:39 } func TestMethod(t *testing.T) { p1:=Per{"aaa",39} p1.getData() } 方法继承 方法是可以继承的,如果匿名字段实现了一个方法,那么包含这个匿名字段的struct也能调用该匿名字段中的方法type Human struct { name, phone string age int } type Stu struct { Human school string } type Employee struct { Human company string } func TestMethod(t *testing.T) { s1:=Stu{Human{"dav","1850103930",7}," 洛阳一中"} s1.SayHi() } func (h *Human) SayHi() { fmt.Printf("我是%s,%d岁,电话%s\n",h.name,h.age,h.phone) } 方法重写 type Human struct { name, phone string age int } type Stu struct { Human school string } type Employee struct { Human company string } func TestMethod(t *testing.T) { s1:=Stu{Human{"dav","1850103930",7}, " 洛阳一中"} s2:=Employee{Human{"dav","1850*****",17},"航天飞机"} s1.SayHi() s2.SayHi() } func (h *Human) SayHi() { fmt.Printf("我是%s,%d岁,电话%s\n",h.name,h.age,h.phone) } func (h *Stu) SayHi() { fmt.Printf("我是%s,%d岁,电话%s,学校%s\n",h.name,h.age,h.phone,h.school) } func (h *Employee) SayHi() { fmt.Printf("我是%s,%d岁,电话%s,工作%s\n",h.name,h.age,h.phone,h.company) } 第9章 Go文件I/O操作9.1文件信息 FileInfo接口 func main() { file:="./layout.html" printMessat(file) } func printMessat(filePath string) { fileinfo,error:=os.Stat(filePath) if error !=nil { fmt.Println("文件打开错误",error.Error()) } fmt.Printf("文件名:%s\n",fileinfo.Name()) fmt.Printf("文件权限:%s\n",fileinfo.Mode()) fmt.Printf("是否为目录:%s\n",fileinfo.IsDir()) fmt.Printf("文件最后修改权限:%s\n",fileinfo.ModTime()) fmt.Printf("文件大小:%s\n",fileinfo.Size()) } 文件路径 file1:="/Users/u51/Documents/go_learn/first.go" file2:="./layout.html" fmt.Printf("是否是绝对路径%v\n",filepath.IsAbs(file1)) fmt.Printf("是否是绝对路径%v\n",filepath.IsAbs(file2)) fmt.Printf("获取文件绝对路径%v\n",filepath.Abs(file2)) fmt.Printf("获取文件绝对路径%v\n",filepath.Abs(file2)) 9.2 文件常规操作 创建目录 os.MKdir() 创建一级目录 os.MKdirAll() 创建多级目录 创建文件 os.create() 创建文件 删除文件 os.Remove() 删除文件或空目录 os.RemoveAll() 移除所有所有路径及包含的子节点,file,error:=os.Create("test1.csv") if error!=nil{ fmt.Printf("文件创建失败") } file.Write([]byte("aaa\n")) file.WriteString("bbbb") file.WriteString("文件") 打开和关闭文件 os.Open从文件开始读取数据,返回值n是实际读取的字节数,如果读到文件末尾n为0或err为io.EOFfile,error:=os.Open("./test1.csv") if error!=nil{ fmt.Printf("打开错误") }else { bs:=make([]byte,1024,1024) for{ n,err:=file.Read(bs) if n==0||err==io.EOF{ fmt.Printf("读取文件结束-----") break } fmt.Println(string(bs[:])) } } defer file.Close() 复制文件 func main() { srcFile:="./test.csv" destFile:="./test1.csv" total,err:=copyFiles(srcFile,destFile) if err!=nil{ fmt.Printf(err.Error()) }else { fmt.Println("复制ok",total) } } func copyFiles(srcfile,destfile string)(int64,error) { file1,err:=os.Open(srcfile) if err!=nil{ return 0, err } file2,err:=os.OpenFile(destfile,os.O_RDWR|os.O_CREATE,os.ModePerm) if err != nil{ return 0, err } defer file1.Close() defer file2.Close() return io.Copy(file2,file1) } 9.3 ioutil包 ioutil包核心函数 方法 作用 ReadFile() 读取文件中所有数据,返回读取的字节数组 WriteFile() 向指定文件写入数据,如果文件不存在,则创建文件,写入文件之前清空文件 ReadDir() 读取一个目录下的所有子文件及目录名称 TempDir() 在当前目录下,创建一个以指定字符串为名称前缀的临时文件夹,并返回文件夹路径 TempFile() 在当前目录下,创建一个以指定字符串为名称的前缀的文件,并以读写模式打开,返回os.File指针 data,error:=ioutil.ReadFile("./test1.csv") if error!=nil{ fmt.Printf("打开错误") }else { fmt.Println(string(data)) } bs:=[]byte("hello中的空间打开") error:=ioutil.WriteFile("./test1.csv",bs,077) if error!=nil{ fmt.Printf("写入文件异常") }else { fmt.Printf("写入文件成功") } 9.4 bufio包bufio实现了带缓冲的I/O操作,达到高效读写 bufio.Reader结构体 bufio.Reader 常用方法 方法 作用 func NewReader(rd io.Reader) *Reader 创建一个具有默认大小缓冲区、从r读取的*Reader func NewReaderSize(rd io.Reader, size int) *Reader 创建一个具有size大小缓冲区,从r读取的*Reader func (b *Reader) Discard(n int) (discarded int, err error) 丢弃n个byte数据 func (b *Reader) Read(p []byte) (n int, err error) 读取n个byte数据 func (b *Reader) Buffered() int 返回缓冲区中现有的可读取的字节数 func (b *Reader) Peek(n int) ([]byte, error) 获取当前缓冲区内接下来的n个byte数据,但不是移动指针 func (b *Reader) ReadByte() (byte, error) 读取1个字节 func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) 读取一行数据,\n分隔 func (b *Reader) ReadString(delim byte) (string, error) 读取1个字符串 func (b *Reader) ReadRune() (r rune, size int, err error) 读取1个utf-8字符 func (b *Reader) Reset(r io.Reader) 清空缓冲区 bufio.Writer结构体 第10章 并行编程并发与并行 并发:同一时间段内执行多个任务 并行:同一时刻执行多个任务 Go语言的并发通过goroutine实现,goroutine类似于线程,属于用户态线程,我们可以根据需要创建成千上万个goroutine并发工作。goroutine是由Go语言的运行时(runtime)调度完成,而线程是由操作系统调度完成。 Go语言还提供channel在多个goroutine间进行通信。goroutine和channel是 Go 语言并发模式的重要实现基础。 goroutine goroutine的概念类似于线程,但 goroutine是由Go的运行时(runtime)调度和管理的。Go程序会智能地将 goroutine 中的任务合理地分配给每个CPU 在Go语言编程中你不需要去自己写进程、线程、协程,你的技能包里只有一个技能–goroutine,当你需要让某个任务并发执行的时候,你只需要把这个任务包装成一个函数,开启一个goroutine去执行这个函数就可以了,就是这么简单粗暴 Go语言中使用goroutine非常简单,只需要在被调用的函数前面加上go 关键字,这样就可以为一个函数创建一个goroutine(也就是创建了一个线程) 一个goroutine必定对应一个函数,可以创建多个goroutine去执行相同的函数。启动单个goroutinepackage main func t1Goroutine() { println("第一个goroutine函数") } func main() { go t1Goroutine() println("main over....") } //输出结果,为什么没有打印函数t1Goroutine中的内容呢???? main over.... 原因: 在程序启动时,Go程序就会为main()函数创建一个默认的goroutine.当main()函数返回的时候该goroutine就结束了,所有在main()中启动的goroutine会一同结束,main函数所在的goroutine就像是权利的游戏中的夜王,其他的goroutine都是异鬼,夜王一死它转化的那些异鬼也就全部GG了。所以我们要想办法让main函数等一等hello函数,最简单粗暴的方式就是time.Sleep了。执行上面代码会发现,先打印main over....然后再打印goroutine的内容 第一个goroutine函数,首先为什么会先打印main over....是因为我们在创建新的goroutine的时候需要花费一些时间,而此时main函数所在的goroutine是继续执行的启动多个goroutinepackage main import ( "fmt" "sync" ) //wait group 用来等待一组goroutines的结束,在主Goroutine里声明,并且设置要等待的goroutine的个数,每个goroutine执行完成之后调用 Done,最后在主Goroutines 里Wait即可 var wg sync.WaitGroup //声明一个全局变量 func t2Goroutine(i int ) { defer wg.Done() fmt.Println("Go ", i) } func main() { for i:=0;i<10;i++{ wg.Add(1) go t2Goroutine(i) } wg.Wait() } //输出结果 Go 5 Go 1 Go 0 Go 7 Go 8 Go 2 Go 6 Go 9 Go 4 Go 3 多次执行上面的代码,会发现每次打印的数字的顺序都不一致。这是因为10个goroutine是并发执行的,而goroutine的调度是随机的。goroutine与线程可增长的栈OS线程(操作系统线程)一般都有固定的栈内存(通常为2MB),一个goroutine的栈在其生命周期开始时只有很小的栈(典型情况下2KB),goroutine的栈不是固定的,他可以按需增大和缩小,goroutine的栈大小限制可以达到1GB,虽然极少会用到这么大。所以在Go语言中一次创建十万左右的goroutine也是可以的。goroutine调度GPM是Go语言运行时(runtime)层面的实现,是go语言自己实现的一套调度系统。区别于操作系统调度OS线程。 G很好理解,就是个goroutine的,里面除了存放本goroutine信息外 还有与所在P的绑定等信息。 P管理着一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用CPU时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他P的队列里抢任务。 M(machine)是Go运行时(runtime)对操作系统内核线程的虚拟, M与内核线程一般是一一映射的关系, 一个groutine最终是要放到M上执行的; P与M一般也是一一对应的。他们关系是: P管理着一组G挂载在M上运行。当一个G长久阻塞在一个M上时,runtime会新建一个M,阻塞G所在的P会把其他的G 挂载在新建的M上。当旧的G阻塞完成或者认为其已经死掉时 回收旧的M。P的个数是通过runtime.GOMAXPROCS设定(最大256),Go1.5版本之后默认为物理线程数。 在并发量大的时候会增加一些P和M,但不会太多,切换太频繁的话得不偿失。单从线程调度讲,Go语言相比起其他语言的优势在于OS线程是由OS内核来调度的,goroutine则是由Go运行时(runtime)自己的调度器调度的,这个调度器使用一个称为m:n调度的技术(复用/调度m个goroutine到n个OS线程)。 其一大特点是goroutine的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的malloc函数(除非内存池需要改变),成本比调度OS线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干goroutine均分在物理线程上, 再加上本身goroutine的超轻量,以上种种保证了go调度方面的性能GOMAXPROCSGo运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码直接在main中声明runtime.GOMAXPROCS(1)channel通道如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。声明channel类型var by1 []int //声明一个int类型的切片 var ch1 chan int //声明一个存放int类型的channel 声明的通道后需要使用make函数初始化之后才能使用。 // 初始化channel ch2:=make(chan int) // 初始化带缓冲区的chan ch3:=make(chan string) channel操作通道有发送(send)、接收(receive)和关闭(close)三种操作。发送和接收都使用<-符号。 现在我们先使用以下语句定义一个通道: ch := make(chan int) 将一个值发送到通道中ch<- 10//把数据10发送到ch中 从一个通道中接收值 data:= <-ch// 从ch中接收的值赋值给变量data <-ch //从ch中接收值,忽略结果 我们通过调用内置的close函数来关闭通道。close(ch) 关于关闭通道需要注意的事情是,只有在通知接收方goroutine所有的数据都发送完毕的时候才需要关闭通道。通道是可以被垃圾回收机制回收的,它和关闭文件是不一样的,在结束操作之后关闭文件是必须要做的,但关闭通道不是必须的。关闭后的通道有以下特点: 对一个关闭的通道再发送值就会导致panic。 对一个关闭的通道进行接收会一直获取值直到通道为空。 对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。 关闭一个已经关闭的通道会导致panic。 无缓冲的通道无缓冲通道又称为阻塞的通道func main() { // 初始化channel ch1:=make(chan int) ch1 <- 10 fmt.Printf("发送成功") } //输出结果 fatal error: all goroutines are asleep - deadlock! goroutine 1 [chan send]: main.main() D:/work/workspace/go_space/goruntine.go:8 +0x5f 为什么会出现deadlock错误呢?因为我们使用ch1 := make(chan int)创建的是无缓冲的通道,无缓冲的通道只有在有人接收值的时候才能发送值。就像你住的小区没有快递柜和代收点,快递员给你打电话必须要把这个物品送到你的手中,简单来说就是无缓冲的通道必须有接收才能发送。上面的代码会阻塞在ch <- 10这一行代码形成死锁,那如何解决这个问题呢?一种方法是启用一个goroutine去接收值,例如:package main import "fmt" func recvData(c chan int) { x:=<-c fmt.Println("接收到的数据",x) } func main() { // 初始化channel ch1:=make(chan int) go recvData(ch1) ch1 <- 10 fmt.Printf("发送成功") } //输出结果 接收到的数据 10 发送成功 无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道有缓冲的通道解决上面问题的方法还有一种,使用带有缓冲区的通道func main() { // 初始化channel ch1:=make(chan int ,2) ch1 <- 10 fmt.Printf("发送成功") } 从通道循环取值当向通道中发送完数据时,我们可以通过close函数来关闭通道。package main import ( "fmt" ) func main() { ch1:=make(chan int) ch2:=make(chan int) // 循环生成100个数,存到ch1中 go func() { for i:=0;i<10;i++{ ch1<-i } close(ch1) }() // 从ch1中接收值,*2后存到ch2 go func() { for{ data,ok:=<-ch1 //通道关闭后再取值ok=false if !ok{ break } ch2<-data*data } close(ch2) }() //在主goroutine中range ch2 for i:=range ch2{// 通道关闭后会退出for range循环 fmt.Println(i) } } //输出结果 0 1 4 9 16 25 36 49 64 81 从上面的例子中我们看到有两种方式在接收值的时候判断该通道是否被关闭,不过我们通常使用的是for range的方式。使用for range遍历通道,当通道被关闭的时候就会退出for range单向通道 chan<- int是一个只写单向通道(只能对其写入int类型值),可以对其执行发送操作但是不能执行接收操作; <-chan int是一个只读单向通道(只能从其读取int类型值),可以对其执行接收操作但是不能执行发送操作。 第11章 反射反射可以在程序编译期将变量的信息如字段名称、类型、结构体信息等整合到可执行文件中,这样就可以在程序运行期获取类型的反射信息Go程序在运行期使用reflect包访问程序的反射信息。在Go语言的反射机制中,任何接口值都由是一个具体类型和具体类型的值两部分组成的。 在Go语言中反射的相关功能由内置的reflect包提供,任意接口值在反射中都可以理解为由reflect.Type和reflect.Value两部分组成,reflect包提供了reflect.TypeOf和reflect.ValueOf两个函数来获取任意对象的Value和Type。reflect.TypeOf() 获取任意值的 类型对象 func reflectType(x interface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v\n", v) } func main() { var a float32 = 3.14 reflectType(a) // type:float32 var b int64 = 100 reflectType(b) // type:int64 } type name和type kind 在反射中关于类型还划分为两种:类型(Type)和种类(Kind)。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)。 举个例子,我们定义了两个指针类型和两个结构体类型,通过反射查看它们的类型和种类。Go语言的反射中像数组、切片、Map、指针等类型的变量,它们的.Name()都是返回空。type person struct { Name string `json:"name"` Age int `json:"age"` } func reflectType(x interface{}) { v := reflect.TypeOf(x) fmt.Printf("type:%v kind:%v\n", v.Name(), v.Kind()) } func main() { var a float32 = 3.14 reflectType(a) // type:float32 var b int64 = 100 reflectType(b) // type:int64 p:=person{ "struc", 20, } reflectType(p) //type:person kind:struct m:=make(map[int]string) reflectType(m) //type: kind:map var s []string reflectType(s) //type: kind:slice } reflect.ValueOf()reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。 func reValueOf(x interface{}) { v:=reflect.ValueOf(x) k:=v.Kind() switch k { case reflect.Int64: // v.Int()从反射中获取整型的原始值,然后通过int64()强制类型转换 fmt.Printf("type is int64, value is %d\n", int64(v.Int())) case reflect.Int32: fmt.Printf("type is int32, value is %d\n", int32(v.Int())) } } func main() { var b int64 = 100 reValueOf(b) // type is int64, value is 100 } 通过反射设置变量值 想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值func relectSetVal(x interface{}) { v:=reflect.ValueOf(x) //反射中使用Elem()方法获取指针对应的值 if v.Elem().Kind()==reflect.Int64{ v.Elem().SetInt(200) } } func main() { var b int64 = 100 relectSetVal(&b) fmt.Println(b) //200 } 结构体反射 任意值通过reflect.TypeOf()获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象(reflect.Type)的NumField()和Field()方法获得结构体成员的详细信息。 type person struct { Name string `json:"name"` Age int `json:"age"` } func main() { p:=person{"test",20} r:=reflect.TypeOf(p) fmt.Println(r.NumField(),r.Name(),r.Kind()) //2 person struct //判断kind是struct if r.Kind()==reflect.Struct{ // 通过for循环遍历结构体的所有字段信息 for i:=0;i<r.NumField();i++{ field := r.Field(i) fmt.Printf("name:%s index:%d type:%v json tag:%v\n", field.Name, field.Index, field.Type, field.Tag.Get("json")) } } // 通过字段名获取指定结构体字段信息 if name_field,ok:=r.FieldByName("Name");ok{ fmt.Printf("name:%s index:%d type:%v json tag:%v\n", name_field.Name, name_field.Index, name_field.Type, name_field.Tag.Get("json")) } } 第12章 worker pool(goroutine池)第13章 网络编程get请求func main() { resp, err := http.Get("http://www.baidu.com/") if err != nil { fmt.Printf("get failed, err:%v\n", err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("read from resp.Body failed, err:%v\n", err) return } fmt.Println(string(body)) } 带参数的get请求关于GET请求的参数需要使用Go语言内置的net/url这个标准库来处理。func httpGet2(requestUrl string) (err error) { Url, err := url.Parse(requestUrl) if err != nil { fmt.Printf("requestUrl parse failed, err:[%s]", err.Error()) return } params := url.Values{} params.Set("username","googlesearch") params.Set("passwd","golang") Url.RawQuery = params.Encode() requestUrl = Url.String() fmt.Printf("requestUrl:[%s]\n", requestUrl) resp, err := http.Get(requestUrl) if err != nil { fmt.Printf("get request failed, err:[%s]", err.Error()) return } defer resp.Body.Close() bodyContent, err := ioutil.ReadAll(resp.Body) fmt.Printf("resp status code:[%d]\n", resp.StatusCode) fmt.Printf("resp body data:[%s]\n", string(bodyContent)) return } 带参数post请求func httpPost(requestUrl string) (err error) { data := url.Values{} data.Add("username", "seemmo") data.Add("passwd", "da123qwe") resp, err := http.PostForm(requestUrl, data) if err != nil { fmt.Printf("get request failed, err:[%s]", err.Error()) return } defer resp.Body.Close() bodyContent, err := ioutil.ReadAll(resp.Body) fmt.Printf("resp status code:[%d]\n", resp.StatusCode) fmt.Printf("resp body data:[%s]\n", string(bodyContent)) return } TCP连接过程TCP服务端程序的处理流程: 1.监听端口 2.接收客户端请求建立连接 3.创建goroutine处理连接 TCP客户端程序的处理流程: 1.建立与服务端的链接 2.进行数据收发 3.关闭链接 服务端代码 package main import ( "bufio" "fmt" "net" ) //实现TCP通信步骤: //1.监听端口 //2.接收客户端请求建立连接 //3.创建goroutine处理连接 func process(conn net.Conn) { defer conn.Close() //延迟关闭 reader:=bufio.NewReader(conn) var b [128]byte for { n,err:=reader.Read(b[:]) if err!=nil{ fmt.Println("read from client failed, err:", err) break } recvStr := string(b[:n]) fmt.Println("收到client端发来的数据:", recvStr) conn.Write([]byte(recvStr)) // 发送数据 } } func main() { listen,err:=net.Listen("tcp","127.0.0.1:9090") if err!=nil{ fmt.Println("listen failed, err:", err) return } for{ conn,err:=listen.Accept() if err!=nil { fmt.Println("accept failed, err", err) continue } go process(conn) } } 客户端代码package main import ( "bufio" "fmt" "net" "os" "strings" ) func main() { conn,err:=net.Dial("tcp","127.0.0.1:9090") if err!=nil{ fmt.Println("err:",err) return } defer conn.Close() fmt.Println("请输入数据") inputReder:=bufio.NewReader(os.Stdin) for{ input,_:=inputReder.ReadString('\n') inputInfo := strings.Trim(input, "\r\n") if strings.ToUpper(inputInfo) == "Q" { // 如果输入q就退出 return } _, err = conn.Write([]byte(inputInfo)) // 发送数据 if err != nil { return } buf := [512]byte{} n, err := conn.Read(buf[:]) if err != nil { fmt.Println("recv failed, err:", err) return } fmt.Println(string(buf[:n])) } } 第14章 数据库操作下载依赖go get -u github.com/go-sql-driver/mysql 创建dbconf包package dbconf import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) // 定义一个全局对象db var db *sql.DB func InitDb() (err error) { dsn := "epai:epai@tcp(10.10.10.48:3306)/bind_dns?charset=utf8mb4&parseTime=True" // 不会校验账号密码是否正确 // 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db db,err = sql.Open("mysql", dsn) if err != nil { return } // 尝试与数据库建立连接(校验dsn是否正确) err = db.Ping() if err != nil { return } return } type binddns struct { zone string data string } func QueryRowDemo() { var dns binddns sqlStr := "SELECT zone, data from dns_records" rows,err:=db.Query(sqlStr) defer rows.Close() if err !=nil { fmt.Printf("query failed, err:%v\n", err) } for rows.Next() { err:=rows.Scan(&dns.zone,&dns.data) if err != nil{ fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("域名:%s 记录值:%s\n", dns.zone,dns.data) } } func InserDemo() { sqlstr := "INSERT INTO `dns_records` VALUES (9, 'abc.com', '*', 'A', '10.10.10.83', 60, NULL, 'any', 255, 28800, 14400, 86400, 86400, 2015050917, 'ddns.net', 'ns.ddns.net.');" res, error := db.Exec(sqlstr) if error != nil { fmt.Printf("insert failed, err:%v\n", error) return } theID, err := res.LastInsertId() // 新插入数据的id if err != nil { fmt.Printf("get lastinsert ID failed, err:%v\n", err) return } fmt.Printf("insert success, the id is %d.\n", theID) } func UpdateDemo() { sqlstr:="update dns_records set data=? where id = ?" res,err:=db.Exec(sqlstr,"10.10.10.98",9) if err !=nil{ fmt.Printf("update failed, err:%v\n", err) return } res_row,err:=res.RowsAffected() if err !=nil{ fmt.Printf("get RowsAffected failed, err:%v\n", err) return } fmt.Printf("update success, affected rows:%d\n", res_row) } 在main.go引入dbconf包package main import ( "fmt" "test_go_mod2/dbconf" ) func main() { err:=dbconf.InitDb() if err != nil { fmt.Printf("init db failed,err:%v\n", err) return }else { fmt.Println("连接mysql成功") } dbconf.QueryRowDemo() dbconf.InserDemo() dbconf.UpdateDemo() } 预处理为什么预处理?优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。 避免SQL注入问题。 Prepare方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。//预查询 func PreareDemo() { sqlStr := "SELECT zone, data from dns_records where id < ?" stmt,err:=db.Prepare(sqlStr) if err !=nil { fmt.Printf("preare query failed, err:%v\n", err) } rows,err:=stmt.Query(8) defer rows.Close() if err !=nil { fmt.Printf("query failed, err:%v\n", err) } for rows.Next() { err:=rows.Scan(&dns.zone,&dns.data) if err != nil{ fmt.Printf("scan failed, err:%v\n", err) return } fmt.Printf("域名:%s 记录值:%s\n", dns.zone,dns.data) } } 15章 gorm操作作者:yum玩坏了链接:https://juejin.cn/post/6903353632687652872来源:稀土掘金著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
2022年06月07日
166 阅读
0 评论
0 点赞
2022-06-05
可视化项目
01-项目介绍 应对现在数据可视化的趋势,越来越多企业需要在很多场景(营销数据,生产数据,用户数据)下使用,可视化图表来展示体现数据,让数据更加直观,数据特点更加突出。我们引入 '立可得' 数据可视化项目。 该项目除了使用了基础的DIV+CSS布局,还引入了一些C3技术,还引入了各类图表的绘制,以及高级的地图数据可视化案例。主要功能有:饼状图、柱状图、线形图、地图 ...02-使用技术完成该项目需要具备以下知识: div + css 布局 flex 布局 css3动画 css3渐变 css3边框图片 原生js + jquery 使用 rem适配 echarts基础 粗略代码统计: css 580行 html 450行 js 400行 (有效) 03-Echarts-介绍ECharts,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。大白话: 是一个JS插件 性能好可流畅运行PC与移动设备 兼容主流浏览器 提供很多常用图表,且可定制。 折线图、柱状图、散点图、饼图、K线图 04-Echarts-体验官方教程:[五分钟上手ECharts](https://www.echartsjs.com/zh/tutorial.html#5 分钟上手 ECharts)自己步骤: 下载echarts https://github.com/apache/incubator-echarts/tree/4.5.0 引入echarts dist/echarts.min.js 准备一个具备大小的DOM容器 <div id="main" style="width: 600px;height:400px;"></div> 初始化echarts实例对象 var myChart = echarts.init(document.getElementById('main')); 指定配置项和数据(option) var option = { xAxis: { type: 'category', data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] }, yAxis: { type: 'value' }, series: [{ data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line' }] }; 将配置项设置给echarts实例对象 myChart.setOption(option); 05-Echarts-基础配置需要了解的主要配置:series xAxis yAxis grid tooltip title legend color series 系列列表。每个系列通过 type 决定自己的图表类型 大白话:图标数据,指定什么类型的图标,可以多个图表重叠。 xAxis:直角坐标系 grid 中的 x 轴 boundaryGap: 坐标轴两边留白策略 true,这时候刻度只是作为分隔线,标签和数据点都会在两个刻度之间的带(band)中间。 yAxis:直角坐标系 grid 中的 y 轴 grid:直角坐标系内绘图网格。 title:标题组件 tooltip:提示框组件 legend:图例组件 color:调色盘颜色列表数据堆叠,同个类目轴上系列配置相同的stack值后 后一个系列的值会在前一个系列的值上相加。 演示代码:var option = { color: ['pink', 'blue', 'green', 'skyblue', 'red'], title: { text: '我的折线图' }, tooltip: { trigger: 'axis' }, legend: { data: ['直播营销', '联盟广告', '视频广告', '直接访问'] }, grid: { left: '3%', right: '3%', bottom: '3%', // 当刻度标签溢出的时候,grid 区域是否包含坐标轴的刻度标签。如果为true,则显示刻度标签 // 如果left right等设置为 0% 刻度标签就溢出了,此时决定是否显示刻度标签 containLabel: true }, toolbox: { feature: { saveAsImage: {} } }, xAxis: { type: 'category', // 坐标轴两边留白策略 true,这时候刻度只是作为分隔线,标签和数据点都会在两个刻度之间的带(band)中间。 boundaryGap: false, data: ['星期一', '星期二', '周三', '周四', '周五', '周六', '周日'] }, yAxis: { type: 'value' }, series: [ { name: '直播营销', // 图表类型是线形图 type: 'line', data: [120, 132, 101, 134, 90, 230, 210] }, { name: '联盟广告', type: 'line', data: [220, 182, 191, 234, 290, 330, 310] }, { name: '视频广告', type: 'line', data: [150, 232, 201, 154, 190, 330, 410] }, { name: '直接访问', type: 'line', data: [320, 332, 301, 334, 390, 330, 320] } ] }; 06-REM适配 设计稿是1920px PC端适配: 宽度在 1024~1920之间页面元素宽高自适应 flexible.js 把屏幕分为 24 等份 cssrem 插件的基准值是 80px插件-配置按钮---配置扩展设置--Root Font Size 里面 设置。但是别忘记重启vscode软件保证生效 要把屏幕宽度约束在1024~1920之间有适配,实现代码: // 实现rem适配 @media screen and (max-width: 1024px) { html { font-size: 42.66px !important; } } @media screen and (min-width: 1920px) { html { font-size: 80px !important; } } 07-基础布局html结构:<body> <div class="viewport"> <div class="column"> <!--概览--> <div></div> <!--监控--> <div></div> <!--点位--> <div></div> </div> <div class="column"> <!--地图--> <div></div> <!--用户--> <div></div> </div> <div class="column"> <!--订单--> <div></div> <!--销售--> <div></div> <div> <!--渠道--> <div></div> <!--季度--> <div></div> </div> <!--排行--> <div></div> </div> </div> </body> 效果图: 1920px * 1078px body 设置背景图 ,行高1.15 viewport 主体容器,限制最小宽度1024px,与最大宽度1920px,最小高度780px。 需要居中显示 使用logo.png做为背景图,在容器内显示 内间距 88px 20px 0 column 列容器,分三列,占比 3:4:3 中间容器外间距 32px 20px 0 css样式:/* 基础布局 */ body{ font-family: Arial, Helvetica, sans-serif; margin: 0; padding: 0; font-size: 0.5rem; line-height: 1.15; background: url(../images/bg.jpg) no-repeat 0 0 / cover; } h4,h3,ul{ margin: 0; padding: 0; font-weight: normal; } ul{ list-style: none; } a{ text-decoration: none; } .viewport{ max-width: 1920px; min-width: 1024px; margin: 0 auto; min-height: 780px; padding: 3.667rem 0.833rem 0; background: url(../images/logo.png) no-repeat 0 0 / contain; display: flex; } .column{ flex: 3; position: relative; } .column:nth-child(2){ flex: 4; margin: 1.333rem 0.833rem 0; } 08-边框图片css3中自适应边框图片运用:组合写法:border-image: url("images/border.jpg") 167/20px round; 拆分写法:border-image-source: url("images/border.jpg"); border-image-slice: 167 167 167 167; border-image-width: 20px; border-image-repeat: round; 解释: 边框图片资源地址 裁剪尺寸(上 右 下 左)单位默认px,可使用百分百。 边框图片的宽度,默认边框的宽度。 平铺方式: stretch 拉伸(默认) repeat 平铺,从边框的中心向两侧开始平铺,会出现不完整的图片。 round 环绕,是完整的使用切割后的图片进行平铺。 DEMO代码:<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>边框图片</title> <style> ul{ margin: 0; padding: 0; list-style: none; } li{ width: 350px; height: 160px; border: 20px solid #ccc; margin-top: 20px; } li:nth-child(1){ /*border-image: url("images/border.jpg") 167/20px round;*/ border-image-source: url("images/border.jpg"); border-image-slice: 167 167 167 167; border-image-width: 20px; /*环绕 是完整的使用切割后的图片进行平铺*/ border-image-repeat: round; } li:nth-child(2){ /*平铺 从边框的中心向两侧开始平铺 会出现不完整的图片*/ border-image: url("images/border.jpg") 167/20px repeat; } li:nth-child(3){ /*默认的平铺方式*/ border-image: url("images/border.jpg") 167/20px stretch; } </style> </head> <body> <ul> <li></li> <li></li> <li></li> </ul> </body> </html> 09-公用面板样式所有的面板的基础样式是一致的,提前布局好。切割示例图: 面板 .panel 容器 .inner 内边距是 上下24px 左右 36px /* 公共面板样式 */ .panel { position: relative; border: 15px solid transparent; border-width: .6375rem .475rem .25rem 1.65rem; border-image-source: url(../images/border.png); border-image-slice: 51 38 20 132; margin-bottom: .25rem; } .inner { position: absolute; top: -0.6375rem; left: -1.65rem; right: -0.475rem; bottom: -0.25rem; padding: .3rem .45rem; } .panel h3 { font-size: 0.25rem; color: #fff; font-weight: 400; } 10-概览区域(overview)-布局html结构: <div class="overview panel"> <div class="inner"> <ul> <li> <h4>2,190</h4> <span> <i class="icon-dot" style="color: #006cff"></i> 设备总数 </span> </li> <li class="item"> <h4>190</h4> <span> <i class="icon-dot" style="color: #6acca3"></i> 季度新增 </span> </li> <li> <h4>3,001</h4> <span> <i class="icon-dot" style="color: #6acca3"></i> 运营设备 </span> </li> <li> <h4>108</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 异常设备 </span> </li> </ul> </div> </div> 样式描述: 容器高度 110px h4字体 28px #fff 左边距 4.8px 下间隙 8px span字体 16px #4c9bfd /* 概览区域 */ .overview { height: 1.375rem; } .overview ul { display: flex; justify-content: space-between; } .overview ul li h4 { font-size: .35rem; color: #fff; margin: 0 0 .1rem .06rem; } .overview ul li span { font-size: .2rem; color: #4c9bfd; } 11-监控区域(monitor)-布局html结构: <!--监控--> <div class="monitor panel"> <div class="inner"> <div class="tabs"> <a href="javascript:;" class="active">故障设备监控</a> <a href="javascript:;" >异常设备监控</a> </div> <div class="content"> <div class="head"> <span class="col">故障时间</span> <span class="col">设备地址</span> <span class="col">异常代码</span> </div> <div class="marquee-view"> <div class="marquee"> <div class="row"> <span class="col">20180701</span> <span class="col">11北京市昌平西路金燕龙写字楼</span> <span class="col">1000001</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190601</span> <span class="col">北京市昌平区城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190704</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000003</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20180701</span> <span class="col">北京市昌平区建路金燕龙写字楼</span> <span class="col">1000004</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000005</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000006</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建西路金燕龙写字楼</span> <span class="col">1000007</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000008</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000009</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190710</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000010</span> <span class="icon-dot"></span> </div> </div> </div> </div> <div class="content"> <div class="head"> <span class="col">异常时间</span> <span class="col">设备地址</span> <span class="col">异常代码</span> </div> <div class="marquee-view"> <div class="marquee"> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000001</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190701</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190703</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190704</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190705</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190706</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190707</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190708</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190709</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> <div class="row"> <span class="col">20190710</span> <span class="col">北京市昌平区建材城西路金燕龙写字楼</span> <span class="col">1000002</span> <span class="icon-dot"></span> </div> </div> </div> </div> </div> </div> 监控区域 monitor 大盒子的高度是 480px结构解释: .tabs 标签选项 加上active激活选项 默认激活第一个选项 .content 切换内容 加上style="display: block;"显示内容 默认激活第一个内容 样式描述: .inner 容器内间距 24px 0 .tabs 容器内间距 0 36px a 容器 颜色: #1950c4 内间距:0 27px 字体:18px 第一个a容器 去除左侧内间距 加上右侧2px宽度边框#00f2f1 激活的时候 颜色白色 .content容器 占满剩余高度 flex:1 默认隐藏 .head 容器 行高 1.05 背景 rgba(255, 255, 255, 0.1) 内间距 12px 36px 颜色 #68d8fe 字体大小 14px row 容器 行高 1.05 内间距 12px 36px 颜色 #68d8ff 字体大小 12px .icon-dot 字体图标 绝对定位 左边0.2rem 透明度0 鼠标经过后:背景 rgba(255, 255, 255, 0.1) 透明度1 col容器 宽度:1rem 2.5rem 1rem 第二个col 一行不换行 溢出 省略 /* 监控区域 */ .monitor{ height: 6rem; } .monitor .inner{ padding: .3rem 0; display: flex; flex-direction: column; } .monitor .tabs{ padding: 0 .45rem; margin-bottom: 0.225rem; display: flex; } .monitor .tabs a{ color:#1950c4; font-size: 0.225rem; padding: 0 .3375rem; } .monitor .tabs a:first-child{ padding-left: 0; border-right: 0.025rem solid #00f2f1; } .monitor .tabs a.active{ color: #fff; } .monitor .content{ flex: 1; position: relative; display: none; } .monitor .head{ display: flex; justify-content: space-between; line-height: 1.05; background-color: rgba(255, 255, 255, 0.1); padding: 0.15rem 0.45rem; color: #68d8fe; font-size: 0.175rem; } .monitor .marquee-view { position: absolute; top: 0.5rem; bottom: 0; width: 100%; overflow: hidden; } .monitor .row{ display: flex; justify-content: space-between; line-height: 1.05; font-size: 0.15rem; color: #61a8ff; padding: 0.15rem 0.45rem; } .monitor .row .icon-dot{ position: absolute; left: 0.2rem; opacity: 0; } .monitor .row:hover { background-color: rgba(255, 255, 255, 0.1); color: #68d8fe; } .monitor .row:hover .icon-dot{ opacity: 1; } .monitor .col:first-child{ width: 1rem; } .monitor .col:nth-child(2){ width: 2.5rem; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } .monitor .col:nth-child(3){ width: 1rem; } 12-监控区域-效果切换功能: 绑定 标签页点击 事件 当前容器加active其他容器移除active index对应的内容容器显示其他容器隐藏 // 切换 $(".monitor .tabs").on("click", "a", function() { $(this).addClass("active").siblings("a").removeClass("active"); // console.log($(this).index()); // 选取对应索引号的content $(".monitor .content").eq($(this).index()).show().siblings(".content").hide(); }); 动画功能: 实现思路: 先克隆列表,追加在后面 marquee-view 占满剩余高度,溢出隐藏 绝对定位,top 1.6rem bottom 0 宽度100%,溢出隐藏 使用animation实现动画 使用 translateY 向上位移 50% 动画时间15s,匀速播放,循环执行。 js代码: // 动画 $(".marquee-view .marquee").each(function() { // console.log($(this)); var rows = $(this).children().clone(); $(this).append(rows); }); css代码:/* 通过CSS3动画滚动marquee */ .marquee-view .marquee { animation: move 15s linear infinite; } @keyframes move { 0% { } 100% { transform: translateY(-50%); } } /* 3.鼠标经过marquee 就停止动画 */ .marquee-view .marquee:hover { animation-play-state: paused; } 13-点位区域(point)-布局html结构: <!-- 点位 --> <div class="point panel"> <div class="inner"> <h3>点位分布统计</h3> <div class="chart"> <div class="pie"></div> <div class="data"> <div class="item"> <h4>320,11</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 点位总数 </span> </div> <div class="item"> <h4>418</h4> <span> <i class="icon-dot" style="color: #eacf19"></i> 本月新增 </span> </div> </div> </div> </div> </div> css样式:point 盒子高度为 340px/* 点位 */ .point { height: 4.25rem; } .point .chart { display: flex; margin-top: 0.3rem; justify-content: space-between; } .point .pie { width: 3.9rem; height: 3rem; margin-left: -0.125rem; background-color: pink; } .point .data { display: flex; flex-direction: column; justify-content: space-between; width: 2.1rem; padding: .45rem .375rem; box-sizing: border-box; background-image: url(../images/rect.png); background-size: cover; } .point h4 { margin-bottom: 0.15rem; font-size: .35rem; color: #fff; } .point span { display: block; color: #4c9bfd; font-size: .2rem; } 14-点位区域-饼图ECharts图表实现步骤: 从官方示例中找到类似图表,适当修改,引入到HTML页面中。 按照产品需求,来定制图表。 1. HTML引入图表// 点位分布统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".pie")); // 2. 指定配置项和数据 var option = { tooltip: { trigger: "item", formatter: "{a} <br/>{b} : {c} ({d}%)" }, series: [ { name: "面积模式", type: "pie", radius: [30, 110], center: ["75%", "50%"], roseType: "area", data: [ { value: 10, name: "rose1" }, { value: 5, name: "rose2" }, { value: 15, name: "rose3" }, { value: 25, name: "rose4" }, { value: 20, name: "rose5" }, { value: 35, name: "rose6" }, { value: 30, name: "rose7" }, { value: 40, name: "rose8" } ] } ] }; // 3. 配置项和数据给我们的实例化对象 myChart.setOption(option); })(); 2. 定制需求第一步:参考官方例子,熟悉里面参数具体含义option = { // 提示框组件 tooltip: { // trigger 触发方式。 非轴图形,使用item的意思是放到数据对应图形上触发提示 trigger: 'item', // 格式化提示内容: // a 代表series系列图表名称 // b 代表series数据名称 data 里面的name // c 代表series数据值 data 里面的value // d代表 当前数据/总数据的比例 formatter: "{a} <br/>{b} : {c} ({d}%)" }, // 控制图表 series: [ { // 图表名称 name: '点位统计', // 图表类型 type: 'pie', // 南丁格尔玫瑰图 有两个圆 内圆半径10% 外圆半径70% // 饼形图半径。 可以是像素。也可以是百分比( 基于DOM容器大小)第一项是内半径,第二项是外半径(通过它可以实现饼形图大小) radius: ['10%', '70%'], // 图表中心位置 left 50% top 50% 距离图表DOM容器 center: ['50%', '50%'], // radius 半径模式,另外一种是 area 面积模式 roseType: 'radius', // 数据集 value 数据的值 name 数据的名称 data: [ {value:10, name:'rose1'}, {value:5, name:'rose2'}, {value:15, name:'rose3'}, {value:25, name:'rose4'}, {value:20, name:'rose5'}, {value:35, name:'rose6'}, {value:30, name:'rose7'}, {value:40, name:'rose8'} ] } ] }; 第二步:按照需求定制 需求1:颜色设置 color: ['#006cff', '#60cda0', '#ed8884', '#ff9f7f', '#0096ff', '#9fe6b8', '#32c5e9', '#1d9dff'], 需求2:修改饼形图大小 ( series对象) radius: ['10%', '70%'], 需求3: 把饼形图的显示模式改为 半径模式 roseType: "radius", 需求4:数据使用更换(series对象 里面 data对象) { value: 20, name: '云南' }, { value: 26, name: '北京' }, { value: 24, name: '山东' }, { value: 25, name: '河北' }, { value: 20, name: '江苏' }, { value: 25, name: '浙江' }, { value: 30, name: '四川' }, { value: 42, name: '湖北' } 需求5:字体略小些 10 px ( series对象里面设置 )饼图图形上的文本标签可以控制饼形图的文字的一些样式。 label 对象设置 series: [ { name: "面积模式", type: "pie", radius: [30, 110], center: ["50%", "50%"], roseType: "radius", // 文本标签控制饼形图文字的相关样式, 注意它是一个对象 label: { fontSize: 10 }, } ] }; 需求6:防止缩放的时候,引导线过长。引导线略短些 (series对象里面的 labelLine 对象设置 ) 连接图表 6 px 连接文字 8 px + // 文字调整 + label:{ + fontSize: 10 + }, + // 引导线调整 + labelLine: { + // 连接扇形图线长 + length: 6, + // 连接文字线长 + length2: 8 + } + } + ], 需求6:浏览器缩放的时候,图表跟着自动适配。 // 监听浏览器缩放,图表对象调用缩放resize函数 window.addEventListener("resize", function() { myChart.resize(); }); 点位统计完整JavaScript代码:// 点位分布统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".pie")); // 2. 指定配置项和数据 var option = { tooltip: { trigger: "item", formatter: "{a} <br/>{b} : {c} ({d}%)" }, // 注意颜色写的位置 color: [ "#006cff", "#60cda0", "#ed8884", "#ff9f7f", "#0096ff", "#9fe6b8", "#32c5e9", "#1d9dff" ], series: [ { name: "点位统计", type: "pie", // 如果radius是百分比则必须加引号 radius: ["10%", "70%"], center: ["50%", "50%"], roseType: "radius", data: [ { value: 20, name: "云南" }, { value: 26, name: "北京" }, { value: 24, name: "山东" }, { value: 25, name: "河北" }, { value: 20, name: "江苏" }, { value: 25, name: "浙江" }, { value: 30, name: "四川" }, { value: 42, name: "湖北" } ], // 修饰饼形图文字相关的样式 label对象 label: { fontSize: 10 }, // 修饰引导线样式 labelLine: { // 连接到图形的线长度 length: 6, // 连接到文字的线长度 length2: 8 } } ] }; // 3. 配置项和数据给我们的实例化对象 myChart.setOption(option); // 4. 当我们浏览器缩放的时候,图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); })(); 15-地图区域 (map) -预留布局html结构: <!-- 地图 --> <div class="map"> <h3> <span class="icon-cube"></span> 设备数据统计 </h3> <div class="chart"> <div class="geo"></div> </div> </div> css样式:/* 地图 */ .map { height: 7.225rem; margin-bottom: 0.25rem; display: flex; flex-direction: column; } .map h3 { line-height: 1; padding: 0.2rem 0; margin: 0; font-size: 0.25rem; color: #fff; font-weight: 400; } .map .icon-cube { color: #68d8fe; } .map .chart { flex: 1; background-color: rgba(255, 255, 255, 0.05); } .map .geo { width: 100%; height: 100%; } 注意第二列(column) 有个外边距(上面 32px 左右 20px 下是 0).viewport .column:nth-child(2) { flex: 4; margin: .4rem .25rem 0; } 16-用户统计 (users) -布局html结构: <!-- 用户 --> <div class="users panel"> <div class="inner"> <h3>全国用户总量统计</h3> <div class="chart"> <div class="bar"></div> <div class="data"> <div class="item"> <h4>120,899</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 用户总量 </span> </div> <div class="item"> <h4>248</h4> <span> <i class="icon-dot" style="color: #eacf19"></i> 本月新增 </span> </div> </div> </div> </div> </div> css样式:/* 用户模块 */ .users { height: 4.25rem; display: flex; } .users .chart { display: flex; margin-top: .3rem; } .users .bar { width: 7.35rem; height: 3rem; } .users .data { display: flex; flex-direction: column; justify-content: space-between; width: 2.1rem; padding: .45rem .375rem; box-sizing: border-box; background-image: url(../images/rect.png); background-size: cover; } .users h4 { margin-bottom: .15rem; font-size: .35rem; color: #fff; } .users span { display: block; color: #4c9bfd; font-size: 0.2rem; } 17-用户统计 (users) -柱状图实现步骤: 从官方示例中找到最接近项目需求的例子,适当修改, 引入到HTML页面中 按照产品需求,来定制图表。 第一步:参考官方示例 + 分析(function () { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".bar")); // 2. 指定配置和数据 var option = { // 工具提示 tooltip: { // 触发类型 经过轴触发axis 经过轴触发item trigger: 'axis', // 轴触发提示才有效 axisPointer: { // 默认为直线,可选为:'line' 线效果 | 'shadow' 阴影效果 type: 'shadow' } }, // 图表边界控制 grid: { // 距离 上右下左 的距离 left: '3%', right: '4%', bottom: '3%', // 是否包含文本 containLabel: true }, // 控制x轴 xAxis: [ { // 使用类目,必须有data属性 type: 'category', // 使用 data 中的数据设为刻度文字 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], // 刻度设置 axisTick: { // true意思:图形在刻度中间 // false意思:图形在刻度之间 alignWithLabel: true } } ], // 控制y轴 yAxis: [ { // 使用数据的值设为刻度文字 type: 'value' } ], // 控制x轴 series: [ { // 图表数据名称 name: '用户统计', // 图表类型 type: 'bar', // 柱子宽度 barWidth: '60%', // 数据 data: [10, 52, 200, 334, 390, 330, 220] } ] }; // 3. 把配置给实例对象 myChart.setOption(option); })(); 第二步:按照需求修改 需求1: 修改柱子的颜色 // 修改线性渐变色方式 1 color: new echarts.graphic.LinearGradient( // (x1,y2) 点到点 (x2,y2) 之间进行渐变 0, 0, 0, 1, [ { offset: 0, color: '#00fffb' }, // 0 起始颜色 { offset: 1, color: '#0061ce' } // 1 结束颜色 ] ), // 修改线性渐变色方式 2 color: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'red' // 0% 处的颜色 }, { offset: 1, color: 'blue' // 100% 处的颜色 }], globalCoord: false // 缺省为 false }, 需求2: 提示框组件放到柱子上触发, 没有阴影等效果 //提示框组件 tooltip: { trigger: 'item', // axisPointer: { // 坐标轴指示器,坐标轴触发有效 这个模块我们此时不需要删掉即可 // type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' // } }, 需求3: 修改柱形图表大小, 以及相关网格。 饼形图修改图表大小是通过 series 对象里面的 radius 柱形图修改图标大小是通过 series 对象里面的 grid 对象 left right 等 显示网格 show: true,网格颜色是 borderColor // 直角坐标系内绘图网格(区域) grid: { top: '3%', right: '3%', bottom: '3%', left: '0%', // 图表位置紧贴画布边缘是否显示刻度以及label文字 防止坐标轴标签溢出跟grid 区域有关系 containLabel: true, // 是否显示直角坐标系网格 show: true, //grid 四条边框的颜色 borderColor: 'rgba(0, 240, 255, 0.3)' }, 需求4: X 轴调整 柱子在刻度之间 剔除刻度不显示 刻度标签文字颜色 #4c9bfd 通过 axisLabel 对象设置 修改x轴线的颜色 axisLine 里面的 lineStyle // 控制x轴 xAxis: [ { // 使用类目,必须有data属性 type: 'category', // 使用 data 中的数据设为刻度文字 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], // 刻度设置 axisTick: { // true意思:图形和刻度居中中间 // false意思:图形在刻度之间 alignWithLabel: false, // 不显示刻度 show: false }, // x坐标轴文字标签样式设置 axisLabel: { color: '#4c9bfd' }, // x坐标轴颜色设置 axisLine:{ lineStyle:{ color:'rgba(0, 240, 255, 0.3)', // width:8, x轴线的粗细 // opcity: 0, 如果不想显示x轴线 则改为 0 } } } 需求5: Y 轴调整 剔除刻度不显示 Y轴文字颜色 #4c9bfd 通过 axisLabel 对象设置 Y轴分割线颜色 splitLine 对象里面 lineStyle 对象设置 // 控制y轴 yAxis: [ { // 使用类目,必须有data属性 type: 'category', // 使用 data 中的数据设为刻度文字 data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], // 刻度设置 axisTick: { // 不显示刻度 show: false }, // y坐标轴文字标签样式设置 axisLabel: { color: '#4c9bfd' }, // y坐标轴颜色设置 axisLine:{ lineStyle:{ color:'rgba(0, 240, 255, 0.3)', // width:8, x轴线的粗细 // opcity: 0, 如果不想显示x轴线 则改为 0 } }, // y轴 分割线的样式 splitLine: { lineStyle: { color: 'rgba(0, 240, 255, 0.3)' } } ], 需求6:调整数据,与省略图形定制 // series data: [2100,1900,1700,1560,1400,1200,1200,1200,900,750,600,480,240] // xAxis data: ['上海', '广州', '北京', '深圳', '合肥', '', '......', '', '杭州', '厦门', '济南', '成都', '重庆'] 省略图形 经过图形才显示提示,且省略的柱子不需要提示 图形单独设置颜色 // 中间省略的数据 准备三项 var item = { name:'', value: 1200, // 柱子颜色 itemStyle: { color: '#254065' }, // 鼠标经过柱子颜色 emphasis: { itemStyle: { color: '#254065' } }, // 工具提示隐藏 tooltip: { extraCssText: 'opacity:0' }, } // series配置data选项在中使用 data: [2100,1900,1700,1560,1400,item,item,item,900,750,600,480,240], // 4. 当我们浏览器缩放的时候,图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); 18-订单区域(order)-布局html结构: <!-- 订单 --> <div class="order panel"> <div class="inner"> <!-- 筛选 --> <div class="filter"> <a href="javascript:;" class="active">365天</a> <a href="javascript:;" >90天</a> <a href="javascript:;" >30天</a> <a href="javascript:;" >24小时</a> </div> <!-- 数据 --> <div class="data"> <div class="item"> <h4>20,301,987</h4> <span> <i class="icon-dot" style="color: #ed3f35;"></i> 订单量 </span> </div> <div class="item"> <h4>99834</h4> <span> <i class="icon-dot" style="color: #eacf19;"></i> 销售额(万元) </span> </div> </div> </div> </div> css样式:/* 订单 */ .order { height: 1.875rem; } .order .filter { display: flex; } .order .filter a { display: block; height: 0.225rem; line-height: 1; padding: 0 0.225rem; color: #1950c4; font-size: 0.225rem; border-right: 0.025rem solid #00f2f1; } .order .filter a:first-child { padding-left: 0; } .order .filter a:last-child { border-right: none; } .order .filter a.active { color: #fff; font-size: 0.25rem; } .order .data { display: flex; margin-top: 0.25rem; } .order .item { width: 50%; } .order h4 { font-size: 0.35rem; color: #fff; margin-bottom: 0.125rem; } .order span { display: block; color: #4c9bfd; font-size: 0.2rem; } 18-订单区域(order)-效果 ( 课后作业 )实现步骤: 提前准备数据 点击后切tab激活样式 点击后切换数据内容 开启定时器动态切换数据 // 订单功能 (function(){ // 1. 准备数据 var data = { day365: { orders: '20,301,987', amount: '99834' }, day90: { orders: '301,987', amount: '9834' }, day30: { orders: '1,987', amount: '3834' }, day1: { orders: '987', amount: '834' } } // 获取显示 订单数量 容器 var $h4Orders = $('.order h4:eq(0)') // 获取显示 金额数量 容器 var $h4Amount = $('.order h4:eq(1)') $('.order').on('click','.filter a',function(){ // 2. 点击切换激活样式 $(this).addClass('active').siblings().removeClass('active') // 3. 点击切换数据 var currdata = data[this.dataset.key] $h4Orders.html(currdata.orders) $h4Amount.html(currdata.amount) }) // 4. 开启定时器切换数据 var index = 0 var $allTab = $('.order .filter a') setInterval(function(){ index ++ if (index >= 4) index = 0 $allTab.eq(index).click() },5000) })(); 19-销售统计( sales )-布局html结构: <!-- 销售额 --> <div class="sales panel"> <div class="inner"> <div class="caption"> <h3>销售额统计</h3> <a href="javascript:;" class="active" >年</a> <a href="javascript:;" >季</a> <a href="javascript:;" >月</a> <a href="javascript:;" >周</a> </div> <div class="chart"> <div class="label">单位:万</div> <div class="line"></div> </div> </div> </div> css样式:/* 销售区域 */ .sales { height: 3.1rem; } .sales .caption { display: flex; line-height: 1; } .sales h3 { height: 0.225rem; padding-right: 0.225rem; border-right: 0.025rem solid #00f2f1; } .sales a { padding: 0.05rem; font-size: 0.2rem; margin: -0.0375rem 0 0 0.2625rem; border-radius: 0.0375rem; color: #0bace6; } .sales a.active { background-color: #4c9bfd; color: #fff; } .sales .inner { display: flex; flex-direction: column; } .sales .chart { flex: 1; padding-top: 0.1875rem; position: relative; } .sales .label { position: absolute; left: 0.525rem; top: 0.225rem; color: #4996f5; font-size: 0.175rem; } .sales .line { width: 100%; height: 100%; } 19-销售统计( sales )-线形图实现步骤: 寻找官方的类似示例,给予分析, 引入到HTML页面中 按照需求来定制它。 **第一步:**寻找官方的类似示例,给予分析。官方参考示例:https://www.echartsjs.com/examples/zh/editor.html?c=line-stack// 销售统计模块 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".line")); // 2. 指定配置和数据 var option = { tooltip: { trigger: "axis" }, legend: { data: ["邮件营销", "联盟广告"] }, grid: { left: "3%", right: "4%", bottom: "3%", containLabel: true }, xAxis: { type: "category", boundaryGap: false, data: ["周一", "周二"] }, yAxis: { type: "value" }, series: [ { name: "邮件营销", type: "line", stack: "总量", data: [120, 132, 101, 134, 90, 230, 210] }, { name: "联盟广告", type: "line", stack: "总量", data: [220, 182, 191, 234, 290, 330, 310] } ] }; // 3. 把配置和数据给实例对象 myChart.setOption(option); })(); **第二步:**按照需求来定制它。 需求1: 修改折线图大小,显示边框设置颜色:#012f4a,并且显示刻度标签。 // 设置网格样式 grid: { top: '20%', left: '3%', right: '4%', bottom: '3%', show: true,// 显示边框 borderColor: '#012f4a',// 边框颜色 containLabel: true // 包含刻度文字在内 }, 需求2: 修改图例组件中的文字颜色 #4c9bfd, 距离右侧 right 为 10% // 图例组件 legend: { textStyle: { color: '#4c9bfd' // 图例文字颜色 }, right: '10%' // 距离右边10% }, 需求3: x轴相关配置 刻度去除 x轴刻度标签字体颜色:#4c9bfd 剔除坐标轴线颜色(将来使用Y轴分割线) 轴两端是不需要内间距 boundaryGap xAxis: { type: 'category', data: ["周一", "周二"], axisTick: { show: false // 去除刻度线 }, axisLabel: { color: '#4c9bfd' // 文本颜色 }, axisLine: { show: false // 去除轴线 }, boundaryGap: false // 去除轴内间距 }, 需求4: y轴的定制 刻度去除 字体颜色:#4c9bfd 分割线颜色:#012f4a yAxis: { type: 'value', axisTick: { show: false // 去除刻度 }, axisLabel: { color: '#4c9bfd' // 文字颜色 }, splitLine: { lineStyle: { color: '#012f4a' // 分割线颜色 } } }, 需求5: 两条线形图定制 颜色分别:#00f2f1 #ed3f35 把折线修饰为圆滑 series 数据中添加 smooth 为 true color: ['#00f2f1', '#ed3f35'], series: [{ name:'预期销售额', data: [820, 932, 901, 934, 1290, 1330, 1320], type: 'line', // 折线修饰为圆滑 smooth: true, },{ name:'实际销售额', data: [100, 331, 200, 123, 233, 543, 400], type: 'line', smooth: true, }] 需求6: 配置数据 // x轴的文字 xAxis: { type: 'category', data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], // 图标数据 series: [{ name:'预期销售额', data: [24, 40, 101, 134, 90, 230, 210, 230, 120, 230, 210, 120], type: 'line', smooth: true },{ name:'实际销售额', data: [40, 64, 191, 324, 290, 330, 310, 213, 180, 200, 180, 79], type: 'line', smooth: true } }] 总结:现在给的是年份数据,还需要切换效果。19-销售统计( sales )-切换效果实现步骤: 准备切换需要依赖的数据 4组 绑定点击事件 切换激活 tab 的样式 切换图表依赖的数据(重新渲染图表) 开启定时器,进行切换, 鼠标经过sales停止定时器,离开开启定时器 第一步:准备数据,使用数据 var data = { year: [ [24, 40, 101, 134, 90, 230, 210, 230, 120, 230, 210, 120], [40, 64, 191, 324, 290, 330, 310, 213, 180, 200, 180, 79] ], quarter: [ [23, 75, 12, 97, 21, 67, 98, 21, 43, 64, 76, 38], [43, 31, 65, 23, 78, 21, 82, 64, 43, 60, 19, 34] ], month: [ [34, 87, 32, 76, 98, 12, 32, 87, 39, 36, 29, 36], [56, 43, 98, 21, 56, 87, 43, 12, 43, 54, 12, 98] ], week: [ [43, 73, 62, 54, 91, 54, 84, 43, 86, 43, 54, 53], [32, 54, 34, 87, 32, 45, 62, 68, 93, 54, 54, 24] ] } series: [{ name:'预期销售额', data: data.year[0], type: 'line', smooth: true, itemStyle: { color: '#00f2f1' } },{ name:'实际销售额', data: data.year[1], type: 'line', smooth: true, itemStyle: { color: '#ed3f35' } }] 第二步:点击后切换 // 切换 $('.sales').on('click', '.caption a', function(){ // 样式 $(this).addClass('active').siblings().removeClass('active') // currData 当前对应的数据 // this.dataset.type 标签上的data-type属性值,对应data中的属性 var currData = data[this.dataset.type] // 修改图表1的数据 option.series[0].data = currData[0] // 修改图表2的数据 option.series[1].data = currData[1] // 重新设置数据 让图标重新渲染 myChart.setOption(option) }) 第三步:tab栏自动切换效果 开启定时器每隔3s,自动让a触发点击事件即可 鼠标经过sales,关闭定时器,离开开启定时器 var as = $(".sales .caption a"); var index = 0; var timer = setInterval(function() { index++; if (index >= 4) index = 0; as.eq(index).click(); }, 1000); // 鼠标经过sales,关闭定时器,离开开启定时器 $(".sales").hover( function() { clearInterval(timer); }, function() { clearInterval(timer); timer = setInterval(function() { index++; if (index >= 4) index = 0; as.eq(index).click(); }, 1000); } ); 自动缩放 // 当我们浏览器缩放的时候,图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); 20-渠道区域&销售进度-布局html结构: <!-- 渠道 季度 --> <div class="wrap"> <div class="channel panel"> <div class="inner"> <h3>渠道分布</h3> <div class="data"> <div class="radar"></div> </div> </div> </div> <div class="quarter panel"> <div class="inner"> <h3>一季度销售进度</h3> <div class="chart"> <div class="box"> <div class="gauge"></div> <div class="label">75<small> %</small></div> </div> <div class="data"> <div class="item"> <h4>1,321</h4> <span> <i class="icon-dot" style="color: #6acca3"></i> 销售额(万元) </span> </div> <div class="item"> <h4>150%</h4> <span> <i class="icon-dot" style="color: #ed3f35"></i> 同比增长 </span> </div> </div> </div> </div> </div> </div> css样式:/* 渠道区块 */ .wrap { display: flex; } .channel, .quarter { flex: 1; height: 2.9rem; } .channel { margin-right: 0.25rem; } .channel .data { overflow: hidden; } .channel .data .radar { height: 2.1rem; width: 100%; background-color: pink; } .channel h4 { color: #fff; font-size: 0.4rem; margin-bottom: 0.0625rem; } .channel small { font-size: 50%; } .channel span { display: block; color: #4c9bfd; font-size: 0.175rem; } /* 季度区块 */ .quarter .inner { display: flex; flex-direction: column; margin: 0 -0.075rem; } .quarter .chart { flex: 1; padding-top: 0.225rem; } .quarter .box { position: relative; } .quarter .label { transform: translate(-50%, -30%); color: #fff; font-size: 0.375rem; position: absolute; left: 50%; top: 50%; } .quarter .label small { font-size: 50%; } .quarter .gauge { height: 1.05rem; } .quarter .data { display: flex; justify-content: space-between; } .quarter .item { width: 50%; } .quarter h4 { color: #fff; font-size: 0.3rem; margin-bottom: 0.125rem; } .quarter span { display: block; width: 100%; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; color: #4c9bfd; font-size: 0.175rem; } 21-渠道分布(channel)-雷达图实现步骤: 寻找官方的类似示例,给予分析,并引入到HTML页面中 按照需求来定制它 第一步: 参考类似实例: https://www.echartsjs.com/examples/zh/editor.html?c=radar-aqi(function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".radar")); // 2.指定配置 var dataBJ = [[55, 9, 56, 0.46, 18, 6, 1]]; var lineStyle = { normal: { width: 1, opacity: 0.5 } }; var option = { backgroundColor: "#161627", radar: { indicator: [ { name: "AQI", max: 300 }, { name: "PM2.5", max: 250 }, { name: "PM10", max: 300 }, { name: "CO", max: 5 }, { name: "NO2", max: 200 }, { name: "SO2", max: 100 } ], shape: "circle", splitNumber: 5, name: { textStyle: { color: "rgb(238, 197, 102)" } }, splitLine: { lineStyle: { color: [ "rgba(238, 197, 102, 0.1)", "rgba(238, 197, 102, 0.2)", "rgba(238, 197, 102, 0.4)", "rgba(238, 197, 102, 0.6)", "rgba(238, 197, 102, 0.8)", "rgba(238, 197, 102, 1)" ].reverse() } }, splitArea: { show: false }, axisLine: { lineStyle: { color: "rgba(238, 197, 102, 0.5)" } } }, series: [ { name: "北京", type: "radar", lineStyle: lineStyle, data: dataBJ, symbol: "none", itemStyle: { color: "#F9713C" }, areaStyle: { opacity: 0.1 } } ] }; // 3.把配置和数据给对象 myChart.setOption(option); })(); 第二步: 按照需求来定制它 需求1: 去掉背景颜色,调整雷达图大小 65% radar:{ center: ['50%', '50%'], // 外半径占据容器大小 radius: '65%', } 需求2: 指示器轴的分割段数为4条(4个圆圈)radar:{ center: ['50%', '50%'], // 外半径占据容器大小 radius: '65%', // 指示器轴的分割段数 splitNumber: 4, } 需求3: 雷达图分割线设为白色半透明 0.5 // 坐标轴在 grid 区域中的分隔线(圆圈) splitLine: { lineStyle: { color: 'rgba(255, 255, 255, 0.5)', // width: 2, // type: 'dashed' } }, 需求4: 雷达图 坐标轴轴线相关设置(竖线) axisLine// 坐标轴轴线相关设置(竖线)axisLine axisLine: { show: true, lineStyle: { color: 'rgba(255, 255, 255, 0.5)' // width: 1, // type: 'solid' } }, 需求5: 修饰雷达图文字颜色为 #4c9bfdname: { // 修饰雷达图文本颜色 textStyle: { color: '#4c9bfd' } }, 需求6: 修饰 区域填充样式 series 对象 区域填充的背景颜色设置为: rgba(238, 197, 102, 0.6) areaStyle: { color: 'rgba(238, 197, 102, 0.6)', }, 区域填充的线条颜色为白色 // 线条样式 lineStyle: { normal: { color: '#fff', // width: 1 } }, 需求7: 标记的图形(拐点)设置 注意 series 里面设置 用圆点显示, 拐点的大小设置为 5 小圆点设置为白色 在小圆点上显示相关数据,颜色设置为白色,10像素 // symbol 标记的样式(拐点),还可以取值'rect' 方块 ,'arrow' 三角等 symbol: 'circle', // 拐点的大小 symbolSize: 5, // 小圆点(拐点)设置为白色 itemStyle: { color: '#fff' }, // 在圆点上显示相关数据 label: { show: true, color: '#fff', fontSize: 10 }, 需求8: 鼠标经过显示提示框组件tooltip: { show: true, // 控制提示框组件的显示位置 position: ['60%', '10%'], }, 需求9: 更换数据 // 雷达图的指示器 内部填充数据 indicator: [ { name: '机场', max: 100 }, { name: '商场', max: 100 }, { name: '火车站', max: 100 }, { name: '汽车站', max: 100 }, { name: '地铁', max: 100 } ], data: [[90, 19, 56, 11, 34]], 整个代码// 销售渠道模块 雷达图 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".radar")); // 2.指定配置 var option = { tooltip: { show: true, // 控制提示框组件的显示位置 position: ["60%", "10%"] }, radar: { indicator: [ { name: "机场", max: 100 }, { name: "商场", max: 100 }, { name: "火车站", max: 100 }, { name: "汽车站", max: 100 }, { name: "地铁", max: 100 } ], // 修改雷达图的大小 radius: "65%", shape: "circle", // 分割的圆圈个数 splitNumber: 4, name: { // 修饰雷达图文字的颜色 textStyle: { color: "#4c9bfd" } }, // 分割的圆圈线条的样式 splitLine: { lineStyle: { color: "rgba(255,255,255, 0.5)" } }, splitArea: { show: false }, // 坐标轴的线修改为白色半透明 axisLine: { lineStyle: { color: "rgba(255, 255, 255, 0.5)" } } }, series: [ { name: "北京", type: "radar", // 填充区域的线条颜色 lineStyle: { normal: { color: "#fff", width: 1, opacity: 0.5 } }, data: [[90, 19, 56, 11, 34]], // 设置图形标记 (拐点) symbol: "circle", // 这个是设置小圆点大小 symbolSize: 5, // 设置小圆点颜色 itemStyle: { color: "#fff" }, // 让小圆点显示数据 label: { show: true, fontSize: 10 }, // 修饰我们区域填充的背景颜色 areaStyle: { color: "rgba(238, 197, 102, 0.6)" } } ] }; // 3.把配置和数据给对象 myChart.setOption(option); // 当我们浏览器缩放的时候,图表也等比例缩放 window.addEventListener("resize", function() { // 让我们的图表调用 resize这个方法 myChart.resize(); }); })(); 22-销售进度 (quarter) -饼状图实现步骤: 寻找官方的类似示例,给予分析,引入到HTML页面中 按照需求来定制它。 第一步:参考官方示例:https://www.echartsjs.com/examples/zh/editor.html?c=pie-doughnut// 销售模块 饼形图 半圆形 设置方式 (function() { // 1. 实例化对象 var myChart = echarts.init(document.querySelector(".gauge")); // 2. 指定数据和配置 var option = { series: [ { name: "销售进度", type: "pie", radius: ["50%", "70%"], //是否启用防止标签重叠策略 // avoidLabelOverlap: false, labelLine: { normal: { show: false } }, data: [{ value: 100 }, { value: 100 }, { value: 200 }] } ] }; // 3. 把数据和配置给实例对象 myChart.setOption(option); })(); 第二步:进行定制需求1:改成半圆,图表大一些,让50%文字在中心。 var option = { series: [ { type: 'pie', // 放大图形 radius: ['130%', '150%'], // 移动下位置 套住50%文字 center: ['48%', '80%'], label: { normal: { show: false } }, // 起始角度,支持范围[0, 360] startAngle: 180, data: [ { value: 100 }, // 不需要名称 { value: 100,}, // 不需要名称 { value: 200, itemStyle: { color: 'transparent' } } // 透明隐藏第三块区域 ] } ] } 需求2:鼠标经过无需变大,修改第一段颜色渐变#00c9e0->#005fc1,修改第二段颜色#12274d。 // 鼠标经过不变大 hoverOffset: 0, data: [ { value: 100, itemStyle: { // 颜色渐变#00c9e0->#005fc1 color: new echarts.graphic.LinearGradient( // (x1,y2) 点到点 (x2,y2) 之间进行渐变 0, 0, 0, 1, [ { offset: 0, color: "#00c9e0" }, // 0 起始颜色 { offset: 1, color: "#005fc1" } // 1 结束颜色 ] ) } }, { value: 100, itemStyle: { color: '#12274d' } }, // 颜色#12274d 23-热销排行(top)-布局html结构: <!-- 排行榜 --> <div class="top panel"> <div class="inner"> <div class="all"> <h3>全国热榜</h3> <ul> <li> <i class="icon-cup1" style="color: #d93f36;"></i> 可爱多 </li> <li> <i class="icon-cup2" style="color: #68d8fe;"></i> 娃哈啥 </li> <li> <i class="icon-cup3" style="color: #4c9bfd;"></i> 喜之郎 </li> </ul> </div> <div class="province"> <h3>各省热销 <i class="date">// 近30日 //</i></h3> <div class="data"> <ul class="sup"> <li> <span>北京</span> <span>25,179 <s class="icon-up"></s></span> </li> <li> <span>河北</span> <span>23,252 <s class="icon-down"></s></span> </li> <li> <span>上海</span> <span>20,760 <s class="icon-up"></s></span> </li> <li> <span>江苏</span> <span>23,252 <s class="icon-down"></s></span> </li> <li> <span>山东</span> <span>20,760 <s class="icon-up"></s></span> </li> </ul> <ul class="sub"> <!-- <li><span></span><span> <s class="icon-up"></s></span></li> --> </ul> </div> </div> </div> </div> css样式:/* 排行榜 */ .top { height: 3.5rem; } .top .inner { display: flex; } .top .all { display: flex; flex-direction: column; width: 2.1rem; color: #4c9bfd; font-size: 0.175rem; vertical-align: middle; } .top .all ul { padding-left: 0.15rem; margin-top: 0.15rem; flex: 1; display: flex; flex-direction: column; justify-content: space-around; } .top .all li { overflow: hidden; } .top .all [class^="icon-"] { font-size: 0.45rem; vertical-align: middle; margin-right: 0.15rem; } .top .province { flex: 1; display: flex; flex-direction: column; color: #fff; } .top .province i { padding: 0 0.15rem; margin-top: 0.0625rem; float: right; font-style: normal; font-size: 0.175rem; color: #0bace6; } .top .province s { display: inline-block; transform: scale(0.8); text-decoration: none; } .top .province .icon-up { color: #dc3c33; } .top .province .icon-down { color: #36be90; } .top .province .data { flex: 1; display: flex; margin-top: 0.175rem; } .top .province ul { flex: 1; line-height: 1; margin-bottom: 0.175rem; } .top .province ul li { display: flex; justify-content: space-between; } .top .province ul span { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .top .province ul.sup { font-size: 0.175rem; } .top .province ul.sup li { color: #4995f4; padding: 0.15rem; } .top .province ul.sup li.active { color: #a3c6f2; background-color: rgba(10, 67, 188, 0.2); } .top .province ul.sub { display: flex; flex-direction: column; justify-content: space-around; font-size: 0.15rem; background-color: rgba(10, 67, 188, 0.2); } .top .province ul.sub li { color: #52ffff; padding: 0.125rem 0.175rem; } .clock { position: absolute; top: -0.45rem; right: 0.5rem; font-size: 0.25rem; color: #0bace6; } .clock i { margin-right: 5px; font-size: 0.25rem; } @media screen and (max-width: 1600px) { .top span { transform: scale(0.9); } .top .province ul.sup li { padding: 0.125rem 0.15rem; } .top .province ul.sub li { padding: 0.0625rem 0.15rem; } .quarter span { transform: scale(0.9); } } 23-热销排行(top)-效果实现思路: 准备后台返回的真实数据 利用数据渲染各省热销模块 sup 模块 (拼接html格式字符串,进行渲染) 当鼠标进入 tab 的时候 激活当前的tab样式,删除其他tab的样式 渲染各省热销 sub 模块 (拼接html格式字符串,进行渲染) 默认激活第一个tab的效果 开启定时器,按依次切换 预备知识: 扩展知识:ES6模版字符 // 模版字符 var star = { name: "刘德华", age: 18 }; // 以前的写法 拼接的时候引号很容易出问题 console.log("我的名字是" + star.name + "我的年龄是" + star.age); // ES6 模板字符写法 console.log(`我的名字是${star.name}我的年龄是${star.age}`); console.log(`<span>${star.name}</span><span>${star.age}</span>`); 开始实现:第一步:得到后台数据(实际开发中,这个数据通过ajax请求获得) var hotData = [ { city: '北京', // 城市 sales: '25, 179', // 销售额 flag: true, // 上升还是下降 brands: [ // 品牌种类数据 { name: '可爱多', num: '9,086', flag: true }, { name: '娃哈哈', num: '8,341', flag: true }, { name: '喜之郎', num: '7,407', flag: false }, { name: '八喜', num: '6,080', flag: false }, { name: '小洋人', num: '6,724', flag: false }, { name: '好多鱼', num: '2,170', flag: true }, ] }, { city: '河北', sales: '23,252', flag: false, brands: [ { name: '可爱多', num: '3,457', flag: false }, { name: '娃哈哈', num: '2,124', flag: true }, { name: '喜之郎', num: '8,907', flag: false }, { name: '八喜', num: '6,080', flag: true }, { name: '小洋人', num: '1,724', flag: false }, { name: '好多鱼', num: '1,170', flag: false }, ] }, { city: '上海', sales: '20,760', flag: true, brands: [ { name: '可爱多', num: '2,345', flag: true }, { name: '娃哈哈', num: '7,109', flag: true }, { name: '喜之郎', num: '3,701', flag: false }, { name: '八喜', num: '6,080', flag: false }, { name: '小洋人', num: '2,724', flag: false }, { name: '好多鱼', num: '2,998', flag: true }, ] }, { city: '江苏', sales: '23,252', flag: false, brands: [ { name: '可爱多', num: '2,156', flag: false }, { name: '娃哈哈', num: '2,456', flag: true }, { name: '喜之郎', num: '9,737', flag: true }, { name: '八喜', num: '2,080', flag: true }, { name: '小洋人', num: '8,724', flag: true }, { name: '好多鱼', num: '1,770', flag: false }, ] }, { city: '山东', sales: '20,760', flag: true, brands: [ { name: '可爱多', num: '9,567', flag: true }, { name: '娃哈哈', num: '2,345', flag: false }, { name: '喜之郎', num: '9,037', flag: false }, { name: '八喜', num: '1,080', flag: true }, { name: '小洋人', num: '4,724', flag: false }, { name: '好多鱼', num: '9,999', flag: true }, ] } ] 第二步:根据数据渲染各省热销 sup 模块内容 删掉原先自带小li 遍历数据 $.each() 拼接字符串把数据渲染到 li 的span 里面 追加给 .sup 盒子 var supHTML = ""; $.each(hotData, function(index, item) { // console.log(item); supHTML += `<li><span>${item.city}</span><span> ${item.sales} <s class= ${item.flag ? "icon-up" : "icon-down"}></s></span></li>`; }); $(".sup").html(supHTML); 第三步:当数据进入 tab 的时候 激活当前的tab样式,删除其他tab的样式 渲染各省热销 sub 模块 注意鼠标进入tab, 只遍历 当前索引号对应的 城市对象里面的 brands 拼接html格式字符串,进行渲染 第四步:默认激活第一个tab // 所有的LI var $lis = $('.province .sup li') // 第一个默认激活 $lis.eq(0).mouseenter() 第五步:开启定时切换 定时器里面 mouseenter 冲突问题的解决方案定时器里面不加mousenter 事件,而是直接重新渲染数据就可以(执行鼠标经过事件里面的代码)最好把渲染的代码封装到函数里面 var index = 0; var timer = setInterval(function() { index++; if (index >= 5) index = 0; // lis.eq(index).mouseenter(); render(lis.eq(index)); }, 2000); $(".province .sup").hover( // 鼠标经过事件 function() { clearInterval(timer); }, // 鼠标离开事件 function() { clearInterval(timer); timer = setInterval(function() { index++; if (index >= 5) index = 0; // lis.eq(index).mouseenter(); render(lis.eq(index)); }, 2000); } ); 24-Echarts-社区介绍社区就是一些,活跃的echart使用者,交流和贡献定制好的图表的地方。 在这里可以找到一些基于echart的高度定制好的图表,相当于基于jquery开发的插件,这里是基于echarts开发的第三方的图表。 25-Echarts-map使用(扩展)参考社区的例子:https://gallery.echartsjs.com/editor.html?c=x0-ExSkZDM (模拟飞机航线)实现步骤: 第一需要下载china.js提供中国地图的js文件 第二个因为里面代码比较多,我们新建一个新的js文件 myMap.js 引入 使用社区提供的配置即可。 需要修改: 去掉图例组件和标题组件 去掉背景颜色 修改地图省份背景 #142957 地图放大通过 zoom 设置为1.2即可 geo: { map: 'china', zoom: 1.2, label: { emphasis: { show: false } }, roam: false, itemStyle: { normal: { areaColor: '#142957', borderColor: '#0692a4' }, emphasis: { areaColor: '#0b1c2d' } } }, 总结:这例子是扩展案例,大家以后可以多看看社区里面的案例。26-总结总结:实现一个需求,需要去推导,具备推导的能力需要练习,时间问题。
2022年06月05日
111 阅读
0 评论
0 点赞
2022-06-02
jQuery事件操作
1.1. jQuery 事件注册 jQuery 为我们提供了方便的事件注册机制,是开发人员抑郁操作优缺点如下: 优点: 操作简单,且不用担心事件覆盖等问题。 缺点: 普通的事件注册不能做事件委托,且无法实现事件解绑,需要借助其他方法。 语法演示代码<body> <div></div> <script> $(function() { // 1. 单个事件注册 $("div").click(function() { $(this).css("background", "purple"); }); $("div").mouseenter(function() { $(this).css("background", "skyblue"); }); }) </script> </body> 1.2. jQuery 事件处理 因为普通注册事件方法的不足,jQuery又开发了多个处理方法,重点讲解如下: on(): 用于事件绑定,目前最好用的事件绑定方法 off(): 事件解绑 trigger() / triggerHandler(): 事件触发 1.2.1 事件处理 on() 绑定事件 因为普通注册事件方法的不足,jQuery又创建了多个新的事件绑定方法bind() / live() / delegate() / on()等,其中最好用的是: on()语法演示代码<body> <div></div> <ul> <li>我们都是好孩子</li> <li>我们都是好孩子</li> <li>我们都是好孩子</li> </ul> <ol></ol> <script> $(function() { // (1) on可以绑定1个或者多个事件处理程序 // $("div").on({ // mouseenter: function() { // $(this).css("background", "skyblue"); // }, // click: function() { // $(this).css("background", "purple"); // } // }); $("div").on("mouseenter mouseleave", function() { $(this).toggleClass("current"); }); // (2) on可以实现事件委托(委派) // click 是绑定在ul 身上的,但是 触发的对象是 ul 里面的小li // $("ul li").click(); $("ul").on("click", "li", function() { alert(11); }); // (3) on可以给未来动态创建的元素绑定事件 $("ol").on("click", "li", function() { alert(11); }) var li = $("<li>我是后来创建的</li>"); $("ol").append(li); }) </script> </body> 1.2.2. 案例:发布微博案例1.点击发布按钮, 动态创建一个小li,放入文本框的内容和删除按钮, 并且添加到ul 中。2.点击的删除按钮,可以删除当前的微博留言。 代码实现略。(详情参考源代码)1.2.3. 事件处理 off() 解绑事件 当某个事件上面的逻辑,在特定需求下不需要的时候,可以把该事件上的逻辑移除,这个过程我们称为事件解绑。jQuery 为我们提供 了多种事件解绑方法:die() / undelegate() / off() 等,甚至还有只触发一次的事件绑定方法 one(),在这里我们重点讲解一下 off() ;语法演示代码<body> <div></div> <ul> <li>我们都是好孩子</li> <li>我们都是好孩子</li> <li>我们都是好孩子</li> </ul> <p>我是一个P标签</p> <script> $(function() { // 事件绑定 $("div").on({ click: function() { console.log("我点击了"); }, mouseover: function() { console.log('我鼠标经过了'); } }); $("ul").on("click", "li", function() { alert(11); }); // 1. 事件解绑 off // $("div").off(); // 这个是解除了div身上的所有事件 $("div").off("click"); // 这个是解除了div身上的点击事件 $("ul").off("click", "li"); // 2. one() 但是它只能触发事件一次 $("p").one("click", function() { alert(11); }) }) </script> </body> 1.2.4. 事件处理 trigger() 自动触发事件 有些时候,在某些特定的条件下,我们希望某些事件能够自动触发, 比如轮播图自动播放功能跟点击右侧按钮一致。可以利用定时器自动触发右侧按钮点击事件,不必鼠标点击触发。由此 jQuery 为我们提供了两个自动触发事件 trigger() 和 triggerHandler() ;语法演示代码<body> <div></div> <input type="text"> <script> $(function() { // 绑定事件 $("div").on("click", function() { alert(11); }); // 自动触发事件 // 1. 元素.事件() // $("div").click();会触发元素的默认行为 // 2. 元素.trigger("事件") // $("div").trigger("click");会触发元素的默认行为 $("input").trigger("focus"); // 3. 元素.triggerHandler("事件") 就是不会触发元素的默认行为 $("input").on("focus", function() { $(this).val("你好吗"); }); // 一个会获取焦点,一个不会 $("div").triggerHandler("click"); // $("input").triggerHandler("focus"); }); </script> </body> 1.3. jQuery 事件对象 jQuery 对DOM中的事件对象 event 进行了封装,兼容性更好,获取更方便,使用变化不大。事件被触发,就会有事件对象的产生。语法演示代码<body> <div></div> <script> $(function() { $(document).on("click", function() { console.log("点击了document"); }) $("div").on("click", function(event) { // console.log(event); console.log("点击了div"); event.stopPropagation(); }) }) </script> </body> 注意:jQuery中的 event 对象使用,可以借鉴 API 和 DOM 中的 event 。1.4. jQuery 拷贝对象 jQuery中分别为我们提供了两套快速获取和设置元素尺寸和位置的API,方便易用,内容如下。语法演示代码 <script> $(function() { // 1.合并数据 var targetObj = {}; var obj = { id: 1, name: "andy" }; // $.extend(target, obj); $.extend(targetObj, obj); console.log(targetObj); // 2. 会覆盖 targetObj 里面原来的数据 var targetObj = { id: 0 }; var obj = { id: 1, name: "andy" }; // $.extend(target, obj); $.extend(targetObj, obj); console.log(targetObj); }) </script> 1.5. jQuery 多库共存 实际开发中,很多项目连续开发十多年,jQuery版本不断更新,最初的 jQuery 版本无法满足需求,这时就需要保证在旧有版本正常运行的情况下,新的功能使用新的jQuery版本实现,这种情况被称为,jQuery 多库共存。语法演示代码<script> $(function() { // 让jquery 释放对$ 控制权 让用自己决定 var suibian = jQuery.noConflict(); console.log(suibian("span")); }) </script> 1.6. jQuery 插件 jQuery 功能比较有限,想要更复杂的特效效果,可以借助于 jQuery 插件完成。 这些插件也是依赖于jQuery来完成的,所以必须要先引入jQuery文件,因此也称为 jQuery 插件。 jQuery 插件常用的网站: jQuery 插件库 http://www.jq22.com/ jQuery 之家 http://www.htmleaf.com/ jQuery 插件使用步骤: 引入相关文件。(jQuery 文件 和 插件文件) 复制相关html、css、js (调用插件)。 1.4.1. 瀑布流插件(重点讲解) 我们学习的第一个插件是jQuery之家的开源插件,瀑布流。我们将重点详细讲解,从找到插件所在网页,然后点击下载代码,到插件的使用等,后面的插件使用可参考瀑布流插件的使用。下载位置代码演示 插件的使用三点: 1. 引入css. 2.引入JS 3.引入html。 (有的简单插件只需引入html和js,甚至有的只需引入js) 1.引入css. <link rel="stylesheet" href="css/normalize.css"> <link rel="stylesheet" type="text/css" href="css/default.css"> <!-- 下面的样式代码为页面布局,可以引入,也可以自己写,自己设计页面样式,一般为直接引入,方便 --> <style type="text/css"> #gallery-wrapper { position: relative; max-width: 75%; width: 75%; margin: 50px auto; } img.thumb { width: 100%; max-width: 100%; height: auto; } .white-panel { position: absolute; background: white; border-radius: 5px; box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.3); padding: 10px; } .white-panel h1 { font-size: 1em; } .white-panel h1 a { color: #A92733; } .white-panel:hover { box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.5); margin-top: -5px; -webkit-transition: all 0.3s ease-in-out; -moz-transition: all 0.3s ease-in-out; -o-transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out; } </style> 2.引入js. <!-- 前两个必须引入 --> <script src="js/jquery-1.11.0.min.js"></script> <script src="js/pinterest_grid.js"></script> <!-- 下面的为启动瀑布流代码,参数可调节属性,具体功能可参考readme.html --> <script type="text/javascript"> $(function() { $("#gallery-wrapper").pinterest_grid({ no_columns: 5, padding_x: 15, padding_y: 10, margin_bottom: 50, single_column_breakpoint: 700 }); }); </script> 3.引入html. <!-- html结构一般为事先写好,很难修改结构,但可以修改内容及图片的多少(article标签) --> <section id="gallery-wrapper"> <article class="white-panel"> <img src="images/P_000.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> <article class="white-panel"> <img src="images/P_005.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> <article class="white-panel"> <img src="images/P_006.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> <article class="white-panel"> <img src="images/P_007.jpg" class="thumb"> <h1><a href="#">我是轮播图片1</a></h1> <p>里面很精彩哦</p> </article> </section> 总结:jQuery插件就是引入别人写好的:html 、css、js (有时也可以只引入一部分,读懂后也可以修改部分内容)1.4.2. 图片懒加载插件 图片的懒加载就是:当页面滑动到有图片的位置,图片才进行加载,用以提升页面打开的速度及用户体验。(下载略)代码演示 懒加载只需引入html 和 js操作 即可,此插件不涉及css。 1.引入js <script src="js/EasyLazyload.min.js"></script> <script> lazyLoadInit({ showTime: 1100, onLoadBackEnd: function(i, e) { console.log("onLoadBackEnd:" + i); }, onLoadBackStart: function(i, e) { console.log("onLoadBackStart:" + i); } }); </script> 2.引入html <img data-lazy-src="upload/floor-1-3.png" alt=""> 1.4.3. 全屏滚动插件 全屏滚动插件比较大,所以,一般大型插件都会有帮助文档,或者网站。全屏滚动插件介绍比较详细的网站为:http://www.dowebok.com/demo/2014/77/代码演示 全屏滚动因为有多重形式,所以不一样的风格html和css也不一样,但是 js 变化不大。所以下面只演示js的引入,html和css引入根据自己实际项目需要使用哪种风格引入对应的HTML和CSS。<script src="js/jquery.min.js"></script> <script src="js/fullpage.min.js"></script> <script> $(function() { $('#dowebok').fullpage({ sectionsColor: ['pink', '#4BBFC3', '#7BAABE', '#f90'], navigation: true }); }); </script> 注意:实际开发,一般复制文件,然后在文件中进行修改和添加功能。1.4.4. bootstrap组件 Bootstrap是 Twitter 公司设计的基于HTML、CSS、JavaScript开发的简洁、直观、强悍的前端开发框架,他依靠jQuery实现,且支持响应式布局,使得 Web 开发更加方便快捷。 凡是在软件开发中用到了软件的复用,被复用的部分都可以称为组件,凡是在应用程序中已经预留接口的组件就是插件。Bootstrap组件使用非常方便: 1.引入bootstrap相关css和js 2.去官网复制html代码演示 引入bootstrap相关css和js <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <script src="bootstrap/js/jquery.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> 去官网复制html的功能模块 <div class="container"> <!-- Single button --> <div class="btn-group"> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> Action <span class="caret"></span> </button> <ul class="dropdown-menu"> <li><a href="#">Action</a></li> <li><a href="#">Another action</a></li> <li><a href="#">Something else here</a></li> <li role="separator" class="divider"></li> <li><a href="#">Separated link</a></li> </ul> </div> </div> 1.4.5. bootstrap插件(JS) bootstrap中的js插件其实也是组件的一部分,只不过是需要js调用功能的组件,所以一般bootstrap的js插件一般会伴随着js代码(有的也可以省略js,用属性实现)。 步骤: 1.引入bootstrap相关css和js 2.去官网复制html 3.复制js代码,启动js插件。代码演示 引入bootstrap相关css和js <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css"> <script src="bootstrap/js/jquery.min.js"></script> <script src="bootstrap/js/bootstrap.min.js"></script> 去官网复制html的功能模块 <!-- 模态框 --> <!-- Large modal --> <button type="button" class="btn btn-primary" data-toggle="modal" data-target=".bs-example-modal-lg">Large modal</button> <div class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel"> <div class="modal-dialog modal-lg" role="document"> <div class="modal-content"> 里面就是模态框 </div> </div> </div> 复制js代码,启动js插件。 <script> // 当我们点击了自己定义的按钮,就弹出模态框 $(".myBtn").on("click", function() { // alert(11); $('#btn').modal() }) </script> 1.4.6. bootstrap案例-阿里百秀1.通过调用组件实现导航栏2.通过调用插件实现登录3.通过调用插件标签页实现 tab 栏 代码实现略。(详情参考源代码)1.7. 综合案例: toDoList案例分析(代码略)1.7.1 案例:案例介绍// 1. 文本框里面输入内容,按下回车,就可以生成待办事项。 // 2. 点击待办事项复选框,就可以把当前数据添加到已完成事项里面。 // 3. 点击已完成事项复选框,就可以把当前数据添加到待办事项里面。 // 4. 但是本页面内容刷新页面不会丢失。 1.7.2 案例:toDoList 分析// 1. 刷新页面不会丢失数据,因此需要用到本地存储 localStorage // 2. 核心思路: 不管按下回车,还是点击复选框,都是把本地存储的数据加载到页面中,这样保证刷新关闭页面不会丢失数据 // 3. 存储的数据格式:var todolist = [{ title : ‘xxx’, done: false}] // 4. 注意点1: 本地存储 localStorage 里面只能存储字符串格式 ,因此需要把对象转换为字符串 JSON.stringify(data)。 // 5. 注意点2: 获取本地存储数据,需要把里面的字符串转换为对象格式JSON.parse() 我们才能使用里面的数据。 1.7.3 案例:toDoList 按下回车把新数据添加到本地存储里面// 1.切记: 页面中的数据,都要从本地存储里面获取,这样刷新页面不会丢失数据,所以先要把数据保存到本地存储里面。 // 2.利用事件对象.keyCode判断用户按下回车键(13)。 // 3.声明一个数组,保存数据。 // 4.先要读取本地存储原来的数据(声明函数 getData()),放到这个数组里面。 // 5.之后把最新从表单获取过来的数据,追加到数组里面。 // 6.最后把数组存储给本地存储 (声明函数 savaDate()) 1.7.4 案例:toDoList 本地存储数据渲染加载到页面// 1.因为后面也会经常渲染加载操作,所以声明一个函数 load,方便后面调用 // 2.先要读取本地存储数据。(数据不要忘记转换为对象格式) // 3.之后遍历这个数据($.each()),有几条数据,就生成几个小li 添加到 ol 里面。 // 4.每次渲染之前,先把原先里面 ol 的内容清空,然后渲染加载最新的数据。 1.7.5 案例:toDoList 删除操作// 1.点击里面的a链接,不是删除的li,而是删除本地存储对应的数据。 // 2.核心原理:先获取本地存储数据,删除对应的数据,保存给本地存储,重新渲染列表li // 3.我们可以给链接自定义属性记录当前的索引号 // 4.根据这个索引号删除相关的数据----数组的splice(i, 1)方法 // 5.存储修改后的数据,然后存储给本地存储 // 6.重新渲染加载数据列表 // 7.因为a是动态创建的,我们使用on方法绑定事件 1.7.6 案例:toDoList 正在进行和已完成选项操作// 1.当我们点击了小的复选框,修改本地存储数据,再重新渲染数据列表。 // 2.点击之后,获取本地存储数据。 // 3.修改对应数据属性 done 为当前复选框的checked状态。 // 4.之后保存数据到本地存储 // 5.重新渲染加载数据列表 // 6.load 加载函数里面,新增一个条件,如果当前数据的done为true 就是已经完成的,就把列表渲染加载到 ul 里面 // 7.如果当前数据的done 为false, 则是待办事项,就把列表渲染加载到 ol 里面 1.7.7 案例:toDoList 统计正在进行个数和已经完成个数// 1.在我们load 函数里面操作 // 2.声明2个变量 :todoCount 待办个数 doneCount 已完成个数 // 3.当进行遍历本地存储数据的时候, 如果 数据done为 false, 则 todoCount++, 否则 doneCount++ // 4.最后修改相应的元素 text() 1.8. 今日总结
2022年06月02日
62 阅读
0 评论
0 点赞
2022-05-29
jQuery操作
1.1. jQuery 属性操作 jQuery 常用属性操作有三种:prop() / attr() / data() ;1.1.1 元素固有属性值 prop() 所谓元素固有属性就是元素本身自带的属性,比如 元素里面的 href ,比如 元素里面的 type。语法 注意:prop() 除了普通属性操作,更适合操作表单属性:disabled / checked / selected 等。1.1.2 元素自定义属性值 attr() 用户自己给元素添加的属性,我们称为自定义属性。 比如给 div 添加 index =“1”。语法 注意:attr() 除了普通属性操作,更适合操作自定义属性。(该方法也可以获取 H5 自定义属性)1.1.3 数据缓存 data() data() 方法可以在指定的元素上存取数据,并不会修改 DOM 元素结构。一旦页面刷新,之前存放的数据都将被移除。语法 注意:同时,还可以读取 HTML5 自定义属性 data-index ,得到的是数字型。演示代码<body> <a href="http://www.itcast.cn" title="都挺好">都挺好</a> <input type="checkbox" name="" id="" checked> <div index="1" data-index="2">我是div</div> <span>123</span> <script> $(function() { //1. element.prop("属性名") 获取元素固有的属性值 console.log($("a").prop("href")); $("a").prop("title", "我们都挺好"); $("input").change(function() { console.log($(this).prop("checked")); }); // console.log($("div").prop("index")); // 2. 元素的自定义属性 我们通过 attr() console.log($("div").attr("index")); $("div").attr("index", 4); console.log($("div").attr("data-index")); // 3. 数据缓存 data() 这个里面的数据是存放在元素的内存里面 $("span").data("uname", "andy"); console.log($("span").data("uname")); // 这个方法获取data-index h5自定义属性 第一个 不用写data- 而且返回的是数字型 console.log($("div").data("index")); }) </script> </body> 1.1.4 案例:购物车案例模块-全选1.全选思路:里面3个小的复选框按钮(j-checkbox)选中状态(checked)跟着全选按钮(checkall)走。2.因为checked 是复选框的固有属性,此时我们需要利用prop()方法获取和设置该属性。3.把全选按钮状态赋值给3小复选框就可以了。4.当我们每次点击小的复选框按钮,就来判断:5.如果小复选框被选中的个数等于3 就应该把全选按钮选上,否则全选按钮不选。6.:checked 选择器 :checked 查找被选中的表单元素。 代码实现略。(详情参考源代码)1.2. jQuery 文本属性值 jQuery的文本属性值常见操作有三种:html() / text() / val() ; 分别对应JS中的 innerHTML 、innerText 和 value 属性。1.2.1 jQuery内容文本值 常见操作有三种:html() / text() / val() ; 分别对应JS中的 innerHTML 、innerText 和 value 属性,主要针对元素的内容还有表单的值操作。语法 注意:html() 可识别标签,text() 不识别标签。演示代码<body> <div> <span>我是内容</span> </div> <input type="text" value="请输入内容"> <script> // 1. 获取设置元素内容 html() console.log($("div").html()); // $("div").html("123"); // 2. 获取设置元素文本内容 text() console.log($("div").text()); $("div").text("123"); // 3. 获取设置表单值 val() console.log($("input").val()); $("input").val("123"); </script> </body> 1.2.2. 案例:购物车案例模块-增减商品数量1.核心思路:首先声明一个变量,当我们点击+号(increment),就让这个值++,然后赋值给文本框。2.注意1: 只能增加本商品的数量, 就是当前+号的兄弟文本框(itxt)的值。3.修改表单的值是val() 方法4.注意2: 这个变量初始值应该是这个文本框的值,在这个值的基础上++。要获取表单的值5.减号(decrement)思路同理,但是如果文本框的值是1,就不能再减了。 代码实现略。(详情参考源代码)1.2.3. 案例:购物车案例模块-修改商品小计1.核心思路:每次点击+号或者-号,根据文本框的值 乘以 当前商品的价格 就是 商品的小计2.注意1: 只能增加本商品的小计, 就是当前商品的小计模块(p-sum)3.修改普通元素的内容是text() 方法4.注意2: 当前商品的价格,要把¥符号去掉再相乘 截取字符串 substr(1)5.parents(‘选择器’) 可以返回指定祖先元素6.最后计算的结果如果想要保留2位小数 通过 toFixed(2) 方法7.用户也可以直接修改表单里面的值,同样要计算小计。 用表单change事件8.用最新的表单内的值 乘以 单价即可 但是还是当前商品小计 代码实现略。(详情参考源代码)1.3. jQuery 元素操作 jQuery 元素操作主要讲的是用jQuery方法,操作标签的遍历、创建、添加、删除等操作。1.3.1. 遍历元素 jQuery 隐式迭代是对同一类元素做了同样的操作。 如果想要给同一类元素做不同操作,就需要用到遍历。语法1 注意:此方法用于遍历 jQuery 对象中的每一项,回调函数中元素为 DOM 对象,想要使用 jQuery 方法需要转换。语法2 注意:此方法用于遍历 jQuery 对象中的每一项,回调函数中元素为 DOM 对象,想要使用 jQuery 方法需要转换。演示代码<body> <div>1</div> <div>2</div> <div>3</div> <script> $(function() { // 如果针对于同一类元素做不同操作,需要用到遍历元素(类似for,但是比for强大) var sum = 0; var arr = ["red", "green", "blue"]; // 1. each() 方法遍历元素 $("div").each(function(i, domEle) { // 回调函数第一个参数一定是索引号 可以自己指定索引号号名称 // console.log(i); // 回调函数第二个参数一定是 dom 元素对象,也是自己命名 // console.log(domEle); // 使用jQuery方法需要转换 $(domEle) $(domEle).css("color", arr[i]); sum += parseInt($(domEle).text()); }) console.log(sum); // 2. $.each() 方法遍历元素 主要用于遍历数据,处理数据 // $.each($("div"), function(i, ele) { // console.log(i); // console.log(ele); // }); // $.each(arr, function(i, ele) { // console.log(i); // console.log(ele); // }) $.each({ name: "andy", age: 18 }, function(i, ele) { console.log(i); // 输出的是 name age 属性名 console.log(ele); // 输出的是 andy 18 属性值 }) }) </script> </body> 1.3.2. 案例:购物车案例模块-计算总计和总额1.把所有文本框中的值相加就是总额数量,总计同理。2.文本框里面的值不同,如果想要相加需要用 each() 遍历,声明一个变量做计数器,累加即可。 代码实现略。(详情参考源代码)1.3.3. 创建、添加、删除 jQuery方法操作元素的创建、添加、删除方法很多,则重点使用部分,如下:语法总和 注意:以上只是元素的创建、添加、删除方法的常用方法,其他方法请参详API。案例代码<body> <ul> <li>原先的li</li> </ul> <div class="test">我是原先的div</div> <script> $(function() { // 1. 创建元素 var li = $("<li>我是后来创建的li</li>"); // 2. 添加元素 // 2.1 内部添加 // $("ul").append(li); 内部添加并且放到内容的最后面 $("ul").prepend(li); // 内部添加并且放到内容的最前面 // 2.2 外部添加 var div = $("<div>我是后妈生的</div>"); // $(".test").after(div); $(".test").before(div); // 3. 删除元素 // $("ul").remove(); 可以删除匹配的元素 自杀 // $("ul").empty(); // 可以删除匹配的元素里面的子节点 孩子 $("ul").html(""); // 可以删除匹配的元素里面的子节点 孩子 }) </script> </body> 1.3.4 案例:购物车案例模块-删除商品模块1.核心思路:把商品remove() 删除元素即可2.有三个地方需要删除: 1. 商品后面的删除按钮 2. 删除选中的商品 3. 清理购物车3.商品后面的删除按钮: 一定是删除当前的商品,所以从 $(this) 出发4.删除选中的商品: 先判断小的复选框按钮是否选中状态,如果是选中,则删除对应的商品5.清理购物车: 则是把所有的商品全部删掉 代码实现略。(详情参考源代码)1.3.5 案例:购物车案例模块-选中商品添加背景1.核心思路:选中的商品添加背景,不选中移除背景即可2.全选按钮点击:如果全选是选中的,则所有的商品添加背景,否则移除背景3.小的复选框点击: 如果是选中状态,则当前商品添加背景,否则移除背景4.这个背景,可以通过类名修改,添加类和删除类 代码实现略。(详情参考源代码)1.4. jQuery 尺寸、位置操作 jQuery中分别为我们提供了两套快速获取和设置元素尺寸和位置的API,方便易用,内容如下。1.4.1. jQuery 尺寸操作 jQuery 尺寸操作包括元素宽高的获取和设置,且不一样的API对应不一样的盒子模型。语法代码演示<body> <div></div> <script> $(function() { // 1. width() / height() 获取设置元素 width和height大小 console.log($("div").width()); // $("div").width(300); // 2. innerWidth() / innerHeight() 获取设置元素 width和height + padding 大小 console.log($("div").innerWidth()); // 3. outerWidth() / outerHeight() 获取设置元素 width和height + padding + border 大小 console.log($("div").outerWidth()); // 4. outerWidth(true) / outerHeight(true) 获取设置 width和height + padding + border + margin console.log($("div").outerWidth(true)); }) </script> </body> 注意:有了这套 API 我们将可以快速获取和子的宽高,至于其他属性想要获取和设置,还要使用 css() 等方法配合。1.4.2. jQuery 位置操作 jQuery的位置操作主要有三个: offset()、position()、scrollTop()/scrollLeft() , 具体介绍如下:语法代码演示<body> <div class="father"> <div class="son"></div> </div> <div class="back">返回顶部</div> <div class="container"></div> <script> $(function() { // 1. 获取设置距离文档的位置(偏移) offset console.log($(".son").offset()); console.log($(".son").offset().top); // $(".son").offset({ // top: 200, // left: 200 // }); // 2. 获取距离带有定位父级位置(偏移) position 如果没有带有定位的父级,则以文档为准 // 这个方法只能获取不能设置偏移 console.log($(".son").position()); // $(".son").position({ // top: 200, // left: 200 // }); // 3. 被卷去的头部 $(document).scrollTop(100); // 被卷去的头部 scrollTop() / 被卷去的左侧 scrollLeft() // 页面滚动事件 var boxTop = $(".container").offset().top; $(window).scroll(function() { // console.log(11); console.log($(document).scrollTop()); if ($(document).scrollTop() >= boxTop) { $(".back").fadeIn(); } else { $(".back").fadeOut(); } }); // 返回顶部 $(".back").click(function() { // $(document).scrollTop(0); $("body, html").stop().animate({ scrollTop: 0 }); // $(document).stop().animate({ // scrollTop: 0 // }); 不能是文档而是 html和body元素做动画 }) }) </script> </body> 1.4.3. 案例:带有动画的返回顶部1.核心原理: 使用animate动画返回顶部。2.animate动画函数里面有个scrollTop 属性,可以设置位置3.但是是元素做动画,因此 $(“body,html”).animate({scrollTop: 0}) 代码实现略。(详情参考源代码)1.4.4. 案例: 品优购电梯导航(上)1.当我们滚动到 今日推荐 模块,就让电梯导航显示出来2.点击电梯导航页面可以滚动到相应内容区域3.核心算法:因为电梯导航模块和内容区模块一一对应的4.当我们点击电梯导航某个小模块,就可以拿到当前小模块的索引号5.就可以把animate要移动的距离求出来:当前索引号内容区模块它的offset().top6.然后执行动画即可 代码实现略。(详情参考源代码)1.4.5. 案例:品优购电梯导航(下)1.当我们点击电梯导航某个小li, 当前小li 添加current类,兄弟移除类名2.当我们页面滚动到内容区域某个模块, 左侧电梯导航,相对应的小li模块,也会添加current类, 兄弟移除current类。3.触发的事件是页面滚动,因此这个功能要写到页面滚动事件里面。4.需要用到each,遍历内容区域大模块。 each里面能拿到内容区域每一个模块元素和索引号5.判断的条件: 被卷去的头部 大于等于 内容区域里面每个模块的offset().top6.就利用这个索引号找到相应的电梯导航小li添加类。 代码实现略。(详情参考源代码)1.5. 今日总结
2022年05月29日
121 阅读
0 评论
0 点赞
2022-05-27
jQuery简介
1.1. jQuery 介绍1.1.1 JavaScript 库 JavaScript库:即 library,是一个封装好的特定的集合(方法和函数)。从封装一大堆函数的角度理解库,就是在这个库中,封装了很多预先定义好的函数在里面,比如动画animate、hide、show,比如获取元素等。简单理解: 就是一个JS 文件,里面对我们原生js代码进行了封装,存放到里面。这样我们可以快速高效的使用这些封装好的功能了。比如 jQuery,就是为了快速方便的操作DOM,里面基本都是函数(方法)。 常见的JavaScript 库:jQuery、Prototype、YUI、Dojo、Ext JS、移动端的zepto等,这些库都是对原生 JavaScript 的封装,内部都是用 JavaScript 实现的,我们主要学习的是 jQuery。1.1.2 jQuery的概念 jQuery总体概况如下 : jQuery 是一个快速、简洁的 JavaScript 库,其设计的宗旨是“write Less,Do More”,即倡导写更少的代码,做更多的事情。 j 就是 JavaScript; Query 查询; 意思就是查询js,把js中的DOM操作做了封装,我们可以快速的查询使用里面的功能。 jQuery 封装了 JavaScript 常用的功能代码,优化了 DOM 操作、事件处理、动画设计和 Ajax 交互。 学习jQuery本质: 就是学习调用这些函数(方法)。 jQuery 出现的目的是加快前端人员的开发速度,我们可以非常方便的调用和使用它,从而提高开发效率。 1.1.3 jQuery的优点 轻量级。核心文件才几十kb,不会影响页面加载速度。 跨浏览器兼容,基本兼容了现在主流的浏览器。 链式编程、隐式迭代。 对事件、样式、动画支持,大大简化了DOM操作。 支持插件扩展开发。有着丰富的第三方的插件,例如:树形菜单、日期控件、轮播图等。 免费、开源。 1.2. jQuery 的基本使用1.2.1 jQuery 的下载 jQuery的官网地址: https://jquery.com/,官网即可下载最新版本。各个版本的下载:https://code.jquery.com/ 版本介绍:1x :兼容 IE 678 等低版本浏览器, 官网不再更新2x :不兼容 IE 678 等低版本浏览器, 官网不再更新3x :不兼容 IE 678 等低版本浏览器, 是官方主要更新维护的版本1.2.2. 体验jQuery 步骤: 引入jQuery文件。 在文档最末尾插入 script 标签,书写体验代码。 $('div').hide() 可以隐藏盒子。 1.2.3. jQuery的入口函数 jQuery中常见的两种入口函数:// 第一种: 简单易用。 $(function () { ... // 此处是页面 DOM 加载完成的入口 }) ; // 第二种: 繁琐,但是也可以实现 $(document).ready(function(){ ... // 此处是页面DOM加载完成的入口 }); 总结: 等着 DOM 结构渲染完毕即可执行内部代码,不必等到所有外部资源加载完成,jQuery 帮我们完成了封装。 相当于原生 js 中的 DOMContentLoaded。 不同于原生 js 中的 load 事件是等页面文档、外部的 js 文件、css文件、图片加载完毕才执行内部代码。 更推荐使用第一种方式。 1.2.4. jQuery中的顶级对象$ $是 jQuery 的别称,在代码中可以使用 jQuery 代替,但一般为了方便,通常都直接使用 $ 。 $是jQuery的顶级对象,相当于原生JavaScript中的 window。把元素利用$包装成jQuery对象,就可以调用jQuery 的方法。 1.2.5. jQuery 对象和 DOM 对象 使用 jQuery 方法和原生JS获取的元素是不一样的,总结如下 : 用原生 JS 获取来的对象就是 DOM 对象 jQuery 方法获取的元素就是 jQuery 对象。 jQuery 对象本质是: 利用$对DOM 对象包装后产生的对象(伪数组形式存储)。 注意:只有 jQuery 对象才能使用 jQuery 方法,DOM 对象则使用原生的 JavaScirpt 方法。1.2.6. jQuery 对象和 DOM 对象转换 DOM 对象与 jQuery 对象之间是可以相互转换的。因为原生js 比 jQuery 更大,原生的一些属性和方法 jQuery没有给我们封装. 要想使用这些属性和方法需要把jQuery对象转换为DOM对象才能使用。// 1.DOM对象转换成jQuery对象,方法只有一种 var box = document.getElementById('box'); // 获取DOM对象 var jQueryObject = $(box); // 把DOM对象转换为 jQuery 对象 // 2.jQuery 对象转换为 DOM 对象有两种方法: // 2.1 jQuery对象[索引值] var domObject1 = $('div')[0] // 2.2 jQuery对象.get(索引值) var domObject2 = $('div').get(0) 总结:实际开发比较常用的是把DOM对象转换为jQuery对象,这样能够调用功能更加强大的jQuery中的方法。1.3. jQuery 选择器 原生 JS 获取元素方式很多,很杂,而且兼容性情况不一致,因此 jQuery 给我们做了封装,使获取元素统一标准。1.3.1. 基础选择器$("选择器") // 里面选择器直接写 CSS 选择器即可,但是要加引号 1.3.2. 层级选择器 层级选择器最常用的两个分别为:后代选择器和子代选择器。 基础选择器和层级选择器案例代码<body> <div>我是div</div> <div class="nav">我是nav div</div> <p>我是p</p> <ul> <li>我是ul 的</li> <li>我是ul 的</li> <li>我是ul 的</li> </ul> <script> $(function() { console.log($(".nav")); console.log($("ul li")); }) </script> </body> 1.3.3. 筛选选择器 筛选选择器,顾名思义就是在所有的选项中选择满足条件的进行筛选选择。常见如下 :案例代码<body> <ul> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> </ul> <ol> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> <li>多个里面筛选几个</li> </ol> <script> $(function() { $("ul li:first").css("color", "red"); $("ul li:eq(2)").css("color", "blue"); $("ol li:odd").css("color", "skyblue"); $("ol li:even").css("color", "pink"); }) </script> </body> 另: jQuery中还有一些筛选方法,类似DOM中的通过一个节点找另外一个节点,父、子、兄以外有所加强。偏重于记忆,演示代码略。1.3.4 知识铺垫 jQuery 设置样式 $('div').css('属性', '值') jQuery 里面的排他思想 // 想要多选一的效果,排他思想:当前元素设置样式,其余的兄弟元素清除样式。 $(this).css(“color”,”red”); $(this).siblings(). css(“color”,””); 隐式迭代 // 遍历内部 DOM 元素(伪数组形式存储)的过程就叫做隐式迭代。 // 简单理解:给匹配到的所有元素进行循环遍历,执行相应的方法,而不用我们再进行循环,简化我们的操作,方便我们调用。 $('div').hide(); // 页面中所有的div全部隐藏,不用循环操作 链式编程 // 链式编程是为了节省代码量,看起来更优雅。 $(this).css('color', 'red').sibling().css('color', ''); 1.3.5 案例:淘宝服饰精品案例思路分析:1.核心原理:鼠标经过左侧盒子某个小li,就让内容区盒子相对应图片显示,其余的图片隐藏。2.需要得到当前小li 的索引号,就可以显示对应索引号的图片3.jQuery 得到当前元素索引号 $(this).index()4.中间对应的图片,可以通过 eq(index) 方法去选择5.显示元素 show() 隐藏元素 hide() 代码实现略。(详情参考源代码)1.4. jQuery 样式操作 jQuery中常用的样式操作有两种:css() 和 设置类样式方法1.4.1. 方法1: 操作 css 方法 jQuery 可以使用 css 方法来修改简单元素样式; 也可以操作类,修改多个样式。 常用以下三种形式 :// 1.参数只写属性名,则是返回属性值 var strColor = $(this).css('color'); // 2. 参数是属性名,属性值,逗号分隔,是设置一组样式,属性必须加引号,值如果是数字可以不用跟单位和引号 $(this).css(''color'', ''red''); // 3. 参数可以是对象形式,方便设置多组样式。属性名和属性值用冒号隔开, 属性可以不用加引号 $(this).css({ "color":"white","font-size":"20px"}); 注意:css() 多用于样式少时操作,多了则不太方便。1.4.2. 方法2: 设置类样式方法 作用等同于以前的 classList,可以操作类样式, 注意操作类里面的参数不要加点。 常用的三种设置类样式方法:// 1.添加类 $("div").addClass("current"); // 2.删除类 $("div").removeClass("current"); // 3.切换类 $("div").toggleClass("current"); 注意: 设置类样式方法比较适合样式多时操作,可以弥补css()的不足。 原生 JS 中 className 会覆盖元素原先里面的类名,jQuery 里面类操作只是对指定类进行操作,不影响原先的类名。 1.4.3. 案例:tab 栏切换思路分析:1.点击上部的li,当前li 添加current类,其余兄弟移除类。2.点击的同时,得到当前li 的索引号3.让下部里面相应索引号的item显示,其余的item隐藏 代码实现略。(详情参考源代码)1.5. jQuery 效果 jQuery 给我们封装了很多动画效果,最为常见的如下: 显示隐藏:show() / hide() / toggle() ; 划入画出:slideDown() / slideUp() / slideToggle() ; 淡入淡出:fadeIn() / fadeOut() / fadeToggle() / fadeTo() ; 自定义动画:animate() ; 注意:动画或者效果一旦触发就会执行,如果多次触发,就造成多个动画或者效果排队执行。jQuery为我们提供另一个方法,可以停止动画排队:stop() ;1.5.1. 显示隐藏 显示隐藏动画,常见有三个方法:show() / hide() / toggle() ; 语法规范如下:代码演示<body> <button>显示</button> <button>隐藏</button> <button>切换</button> <div></div> <script> $(function() { $("button").eq(0).click(function() { $("div").show(1000, function() { alert(1); }); }) $("button").eq(1).click(function() { $("div").hide(1000, function() { alert(1); }); }) $("button").eq(2).click(function() { $("div").toggle(1000); }) // 一般情况下,我们都不加参数直接显示隐藏就可以了 }); </script> </body> 1.5.2. 滑入滑出 滑入滑出动画,常见有三个方法:slideDown() / slideUp() / slideToggle() ; 语法规范如下:代码演示<body> <button>下拉滑动</button> <button>上拉滑动</button> <button>切换滑动</button> <div></div> <script> $(function() { $("button").eq(0).click(function() { // 下滑动 slideDown() $("div").slideDown(); }) $("button").eq(1).click(function() { // 上滑动 slideUp() $("div").slideUp(500); }) $("button").eq(2).click(function() { // 滑动切换 slideToggle() $("div").slideToggle(500); }); }); </script> </body> 小案例:下拉菜单略(详情参看源码)。1.5.3 淡入淡出 淡入淡出动画,常见有四个方法:fadeIn() / fadeOut() / fadeToggle() / fadeTo() ; 语法规范如下:代码演示<body> <button>淡入效果</button> <button>淡出效果</button> <button>淡入淡出切换</button> <button>修改透明度</button> <div></div> <script> $(function() { $("button").eq(0).click(function() { // 淡入 fadeIn() $("div").fadeIn(1000); }) $("button").eq(1).click(function() { // 淡出 fadeOut() $("div").fadeOut(1000); }) $("button").eq(2).click(function() { // 淡入淡出切换 fadeToggle() $("div").fadeToggle(1000); }); $("button").eq(3).click(function() { // 修改透明度 fadeTo() 这个速度和透明度要必须写 $("div").fadeTo(1000, 0.5); }); }); </script> </body> 1.5.4 自定义动画 自定义动画非常强大,通过参数的传递可以模拟以上所有动画,方法为:animate() ; 语法规范如下:代码演示<body> <button>动起来</button> <div></div> <script> $(function() { $("button").click(function() { $("div").animate({ left: 500, top: 300, opacity: .4, width: 500 }, 500); }) }) </script> </body> 1.5.5 停止动画排队 动画或者效果一旦触发就会执行,如果多次触发,就造成多个动画或者效果排队执行。 停止动画排队的方法为:stop() ; stop() 方法用于停止动画或效果。 stop() 写到动画或者效果的前面, 相当于停止结束上一次的动画。 总结: 每次使用动画之前,先调用 stop() ,在调用动画。1.5.6. 事件切换 jQuery中为我们添加了一个新事件 hover() ; 功能类似 css 中的伪类 :hover 。介绍如下语法hover([over,]out) // 其中over和out为两个函数 over:鼠标移到元素上要触发的函数(相当于mouseenter) out:鼠标移出元素要触发的函数(相当于mouseleave) 如果只写一个函数,则鼠标经过和离开都会触发它 hover事件和停止动画排列案例<body> <ul class="nav"> <li> <a href="#">微博</a> <ul><li><a href="">私信</a></li><li><a href="">评论</a></li><li><a href="">@我</a></li></ul> </li> <li> <a href="#">微博</a> <ul><li><a href="">私信</a></li><li><a href="">评论</a></li><li><a href="">@我</a></li></ul> </li> </ul> <script> $(function() { // 鼠标经过 // $(".nav>li").mouseover(function() { // // $(this) jQuery 当前元素 this不要加引号 // // show() 显示元素 hide() 隐藏元素 // $(this).children("ul").slideDown(200); // }); // // 鼠标离开 // $(".nav>li").mouseout(function() { // $(this).children("ul").slideUp(200); // }); // 1. 事件切换 hover 就是鼠标经过和离开的复合写法 // $(".nav>li").hover(function() { // $(this).children("ul").slideDown(200); // }, function() { // $(this).children("ul").slideUp(200); // }); // 2. 事件切换 hover 如果只写一个函数,那么鼠标经过和鼠标离开都会触发这个函数 $(".nav>li").hover(function() { // stop 方法必须写到动画的前面 $(this).children("ul").stop().slideToggle(); }); }) </script> </body> 1.5.7. 案例:王者荣耀手风琴效果思路分析:1.鼠标经过某个小li 有两步操作:2.当前小li 宽度变为 224px, 同时里面的小图片淡出,大图片淡入3.其余兄弟小li宽度变为69px, 小图片淡入, 大图片淡出 代码实现略。(详情参考源代码)1.6. 今日总结
2022年05月27日
89 阅读
0 评论
0 点赞
2022-05-27
Web APIs本地存储
# Web APIs本地存储## **1.1. 触屏事件**### 1.1.1 触屏事件概述移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android 和 IOS 都有。touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。常见的触屏事件如下:### 1.1.2 触摸事件对象(TouchEvent)TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等touchstart、touchmove、touchend 三个事件都会各自有事件对象。触摸事件对象重点我们看三个常见对象列表:> **因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes**### 1.1.3 移动端拖动元素1. touchstart、touchmove、touchend 可以实现拖动元素 2. 但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageX 和 pageY 3. 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离 4. 手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置拖动元素三步曲:(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子(3) 离开手指 touchend:> **注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();**## 1.2. 移动端常见特效### 1.2.1 案例: 移动轮播图`移动端轮播图功能和基本PC端一致。`1. 可以自动播放图片 2. 手指可以拖动播放轮播图### 1.2.2. 案例分析:1. 自动播放功能 2. 开启定时器 3. 移动端移动,可以使用translate 移动 4. 想要图片优雅的移动,请添加过渡效果1. 自动播放功能-无缝滚动2. 注意,我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断3. 此时需要添加检测过渡完成事件 transitionend 4. 判断条件:如果索引号等于 3 说明走到最后一张图片,此时 索引号要复原为 05. 此时图片,去掉过渡效果,然后移动6. 如果索引号小于0, 说明是倒着走, 索引号等于27. 此时图片,去掉过渡效果,然后移动## 1.2.3 classList 属性classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。该属性用于在元素中添加,移除及切换 CSS 类。有以下方法**添加类:**element.classList.add(’类名’);```javascript focus.classList.add('current'); ```**移除类:**element.classList.remove(’类名’);```javascript focus.classList.remove('current'); ```**切换类:**element.classList.toggle(’类名’);```javascript focus.classList.toggle('current'); ````注意:以上方法里面,所有类名都不带点`### 1.2.4. 案例分析1. 小圆点跟随变化效果2. 把ol里面li带有current类名的选出来去掉类名 remove3. 让当前索引号的小li 加上 current add4. 但是,是等着过渡结束之后变化,所以这个写到 transitionend 事件里面1. 手指滑动轮播图 2. 本质就是ul跟随手指移动,简单说就是移动端拖动元素 3. 触摸元素touchstart: 获取手指初始坐标 4. 移动手指touchmove: 计算手指的滑动距离,并且移动盒子 5. 离开手指touchend: 根据滑动的距离分不同的情况 6. 如果移动距离小于某个像素 就回弹原来位置 7. 如果移动距离大于某个像素就上一张下一张滑动。 8. 滑动也分为左滑动和右滑动判断的标准是 移动距离正负 如果是负值就是左滑 反之右滑 9. 如果是左滑就播放下一张 (index++) 10. 如果是右滑就播放上一张 (index--)### 1.3.1. 案例:返回顶部当页面滚动某个地方,就显示,否则隐藏点击可以返回顶部### 1.3.2.案例分析1. 滚动某个地方显示 2. 事件:scroll页面滚动事件 3. 如果被卷去的头部(window.pageYOffset )大于某个数值 4. 点击,window.scroll(0,0) 返回顶部## 1.4. click 延时解决方案移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。解决方案: 1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟。```html ``` 2.利用touch事件自己封装这个事件解决300ms 延迟。 原理就是:1. 当我们手指触摸屏幕,记录当前触摸时间 2. 当我们手指离开屏幕, 用离开的时间减去触摸的时间 3. 如果时间小于150ms,并且没有滑动过屏幕, 那么我们就定义为点击代码如下:```javascript //封装tap,解决click 300ms 延时 function tap (obj, callback) { var isMove = false; var startTime = 0; // 记录触摸时候的时间变量 obj.addEventListener('touchstart', function (e) { startTime = Date.now(); // 记录触摸时间 }); obj.addEventListener('touchmove', function (e) { isMove = true; // 看看是否有滑动,有滑动算拖拽,不算点击 }); obj.addEventListener('touchend', function (e) { if (!isMove && (Date.now() - startTime) < 150) { // 如果手指触摸和离开时间小于150ms 算点击 callback && callback(); // 执行回调函数 } isMove = false; // 取反 重置 startTime = 0; }); } //调用 tap(div, function(){ // 执行代码 });```3. 使用插件。fastclick 插件解决300ms 延迟。## 1.5. 移动端常用开发插件### 1.5.1. 什么是插件移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我完成操作,那么什么是插件呢?JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。我们以前写的animate.js 也算一个最简单的插件fastclick 插件解决 300ms 延迟。 使用延时GitHub官网地址: [https://](https://github.com/ftlabs/fastclick)[github.com/ftlabs/fastclick](https://github.com/ftlabs/fastclick)### 1.5.2. 插件的使用1. 引入 js 插件文件。2. 按照规定语法使用。3. fastclick 插件解决 300ms 延迟。 使用延时4. GitHub官网地址: https://github.com/ftlabs/fastclick```javascript if ('addEventListener' in document) { document.addEventListener('DOMContentLoaded', function() { FastClick.attach(document.body); }, false); } ```### 1.5.3. Swiper 插件的使用中文官网地址: https://www.swiper.com.cn/1. 引入插件相关文件。 2. 按照规定语法使用### 1.5.4. 其他移动端常见插件lsuperslide: http://www.superslide2.com/l iscroll: https://github.com/cubiq/iscroll### 1.5.5. 插件的使用总结1.确认插件实现的功能2.去官网查看使用说明3.下载插件4.打开demo实例文件,查看需要引入的相关文件,并且引入5.复制demo实例文件中的结构html,样式css以及js代码### 1.5.6. 移动端视频插件 zy.media.jsH5 给我们提供了 video 标签,但是浏览器的支持情况不同。不同的视频格式文件,我们可以通过source解决。但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。这个时候我们可以使用插件方式来制作。我们可以通过 JS 修改元素的大小、颜色、位置等样式。## 1.6. 移动端常用开发框架### 1.6.1. 移动端视频插件 zy.media.js框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能开发PC端,也能开发移动端前端常用的移动端插件有 swiper、superslide、iscroll等。框架: 大而全,一整套解决方案插件: 小而专一,某个功能的解决方案### 1.6.2. BootstrapBootstrap 是一个简洁、直观、强悍的前端开发框架,它让 web 开发更迅速、简单。它能开发PC端,也能开发移动端Bootstrap JS插件使用步骤:1.引入相关js 文件2.复制HTML 结构3.修改对应样式4.修改相应JS 参数## 1.7. 本地存储随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案。### 1.7.1.本地存储特性1、数据存储在用户浏览器中2、设置、读取方便、甚至页面刷新不丢失数据3、容量较大,sessionStorage约5M、localStorage约20M4、只能存储字符串,可以将对象JSON.stringify() 编码后存储### 1.7.2.window.sessionStorage1、生命周期为关闭浏览器窗口2、在同一个窗口(页面)下数据可以共享3、以键值对的形式存储使用存储数据:```javascript sessionStorage.setItem(key, value) ```获取数据:```javascript sessionStorage.getItem(key) ```删除数据:```javascript sessionStorage.removeItem(key) ```清空数据:(所有都清除掉)```javascript sessionStorage.clear() ```### 1.7.3.window.localStorage1、声明周期永久生效,除非手动删除 否则关闭页面也会存在2、可以多窗口(页面)共享(同一浏览器可以共享)3. 以键值对的形式存储使用存储数据:```javascript localStorage.setItem(key, value) ```获取数据:```javascript localStorage.getItem(key) ```删除数据:```javascript localStorage.removeItem(key) ```清空数据:(所有都清除掉)```javascript localStorage.clear() ```### 1.7.4.案例:记住用户名如果勾选记住用户名, 下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名#### 案例分析1. 把数据存起来,用到本地存储2. 关闭页面,也可以显示用户名,所以用到localStorage3. 打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框4. 当复选框发生改变的时候change事件5. 如果勾选,就存储,否则就移除
2022年05月27日
84 阅读
0 评论
0 点赞
2022-05-21
Web APIS 练习
**1.1. **动画函数封装1.1.1 缓动效果原理缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来思路: 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。 核心算法: (目标值 - 现在的位置) / 10 做为每次移动的距离步长 停止的条件是: 让当前盒子位置等于目标位置就停止定时器 注意步长值需要取整 1.1.2 动画函数多个目标值之间移动可以让动画函数从 800 移动到 500。当我们点击按钮时候,判断步长是正值还是负值 1.如果是正值,则步长往大了取整 2.如果是负值,则步长 向小了取整1.1.3 动函数添加回调函数回调函数原理:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。回调函数写的位置:定时器结束的位置。1.1.4 动画完整版代码:function animate(obj, target, callback) { // console.log(callback); callback = function() {} 调用的时候 callback() // 先清除以前的定时器,只保留当前的一个定时器执行 clearInterval(obj.timer); obj.timer = setInterval(function() { // 步长值写到定时器的里面 // 把我们步长值改为整数 不要出现小数的问题 // var step = Math.ceil((target - obj.offsetLeft) / 10); var step = (target - obj.offsetLeft) / 10; step = step > 0 ? Math.ceil(step) : Math.floor(step); if (obj.offsetLeft == target) { // 停止动画 本质是停止定时器 clearInterval(obj.timer); // 回调函数写到定时器结束里面 // if (callback) { // // 调用函数 // callback(); // } callback && callback(); } // 把每次加1 这个步长值改为一个慢慢变小的值 步长公式:(目标值 - 现在的位置) / 10 obj.style.left = obj.offsetLeft + step + 'px'; }, 15); } 1.2. 常见网页特效案例1.2.1 案例:网页轮播图轮播图也称为焦点图,是网页中比较常见的网页特效。功能需求: 1.鼠标经过轮播图模块,左右按钮显示,离开隐藏左右按钮。 2.点击右侧按钮一次,图片往左播放一张,以此类推,左侧按钮同理。 3.图片播放的同时,下面小圆圈模块跟随一起变化。 4.点击小圆圈,可以播放相应图片。 5.鼠标不经过轮播图,轮播图也会自动播放图片。 6.鼠标经过,轮播图模块, 自动播放停止。window.addEventListener('load', function() { // 1. 获取元素 var arrow_l = document.querySelector('.arrow-l'); var arrow_r = document.querySelector('.arrow-r'); var focus = document.querySelector('.focus'); var focusWidth = focus.offsetWidth; // 2. 鼠标经过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); }); // 3. 动态生成小圆圈 有几张图片,我就生成几个小圆圈 var ul = focus.querySelector('ul'); var ol = focus.querySelector('.circle'); // console.log(ul.children.length); for (var i = 0; i < ul.children.length; i++) { // 创建一个小li var li = document.createElement('li'); // 记录当前小圆圈的索引号 通过自定义属性来做 li.setAttribute('index', i); // 把小li插入到ol 里面 ol.appendChild(li); // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件 li.addEventListener('click', function() { // 干掉所有人 把所有的小li 清除 current 类名 for (var i = 0; i < ol.children.length; i++) { ol.children[i].className = ''; } // 留下我自己 当前的小li 设置current 类名 this.className = 'current'; // 5. 点击小圆圈,移动图片 当然移动的是 ul // ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值 // 当我们点击了某个小li 就拿到当前小li 的索引号 var index = this.getAttribute('index'); // 当我们点击了某个小li 就要把这个li 的索引号给 num num = index; // 当我们点击了某个小li 就要把这个li 的索引号给 circle circle = index; // num = circle = index; console.log(focusWidth); console.log(index); animate(ul, -index * focusWidth); }) } // 把ol里面的第一个小li设置类名为 current ol.children[0].className = 'current'; // 6. 克隆第一张图片(li)放到ul 最后面 var first = ul.children[0].cloneNode(true); ul.appendChild(first); // 7. 点击右侧按钮, 图片滚动一张 var num = 0; // circle 控制小圆圈的播放 var circle = 0; // flag 节流阀 var flag = true; arrow_r.addEventListener('click', function() { if (flag) { flag = false; // 关闭节流阀 // 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0 if (num == ul.children.length - 1) { ul.style.left = 0; num = 0; } num++; animate(ul, -num * focusWidth, function() { flag = true; // 打开节流阀 }); // 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放 circle++; // 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原 if (circle == ol.children.length) { circle = 0; } // 调用函数 circleChange(); } }); // 9. 左侧按钮做法 arrow_l.addEventListener('click', function() { if (flag) { flag = false; if (num == 0) { num = ul.children.length - 1; ul.style.left = -num * focusWidth + 'px'; } num--; animate(ul, -num * focusWidth, function() { flag = true; }); // 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放 circle--; // 如果circle < 0 说明第一张图片,则小圆圈要改为第4个小圆圈(3) // if (circle < 0) { // circle = ol.children.length - 1; // } circle = circle < 0 ? ol.children.length - 1 : circle; // 调用函数 circleChange(); } }); function circleChange() { // 先清除其余小圆圈的current类名 for (var i = 0; i < ol.children.length; i++) { ol.children[i].className = ''; } // 留下当前的小圆圈的current类名 ol.children[circle].className = 'current'; } // 10. 自动播放轮播图 var timer = setInterval(function() { //手动调用点击事件 arrow_r.click(); }, 2000); }) 1.2.2. 节流阀防止轮播图按钮连续点击造成播放过快。节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。 开始设置一个变量var flag= true;If(flag){flag = false; do something} 关闭水龙头利用回调函数动画执行完毕, flag = true 打开水龙头1.2.3. 案例:返回顶部 带有动画的返回顶部 此时可以继续使用我们封装的动画函数 只需要把所有的left 相关的值改为 跟 页面垂直滚动距离相关就可以了 页面滚动了多少,可以通过 window.pageYOffset 得到 最后是页面滚动,使用 window.scroll(x,y) //1. 获取元素 var sliderbar = document.querySelector('.slider-bar'); var banner = document.querySelector('.banner'); // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面 var bannerTop = banner.offsetTop // 当我们侧边栏固定定位之后应该变化的数值 var sliderbarTop = sliderbar.offsetTop - bannerTop; // 获取main 主体元素 var main = document.querySelector('.main'); var goBack = document.querySelector('.goBack'); var mainTop = main.offsetTop; // 2. 页面滚动事件 scroll document.addEventListener('scroll', function() { // console.log(11); // window.pageYOffset 页面被卷去的头部 // console.log(window.pageYOffset); // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位 if (window.pageYOffset >= bannerTop) { sliderbar.style.position = 'fixed'; sliderbar.style.top = sliderbarTop + 'px'; } else { sliderbar.style.position = 'absolute'; sliderbar.style.top = '300px'; } // 4. 当我们页面滚动到main盒子,就显示 goback模块 if (window.pageYOffset >= mainTop) { goBack.style.display = 'block'; } else { goBack.style.display = 'none'; } }) // 3. 当我们点击了返回顶部模块,就让窗口滚动的页面的最上方 goBack.addEventListener('click', function() { // 里面的x和y 不跟单位的 直接写数字即可 // window.scroll(0, 0); // 因为是窗口滚动 所以对象是window animate(window, 0); }); 1.2.4. 案例:筋头云案例 利用动画函数做动画效果 原先筋斗云的起始位置是0 鼠标经过某个小li,把当前小li的offsetLeft 位置做为目标值即可 鼠标离开某个小li,就把目标值设为 0 如果点击了某个小li, 就把li当前的位置存储起来,做为筋斗云的起始位置 window.addEventListener('load', function() { // 1. 获取元素 var cloud = document.querySelector('.cloud'); var c_nav = document.querySelector('.c-nav'); var lis = c_nav.querySelectorAll('li'); // 2. 给所有的小li绑定事件 // 这个current 做为筋斗云的起始位置 var current = 0; for (var i = 0; i < lis.length; i++) { // (1) 鼠标经过把当前小li 的位置做为目标值 lis[i].addEventListener('mouseenter', function() { animate(cloud, this.offsetLeft); }); // (2) 鼠标离开就回到起始的位置 lis[i].addEventListener('mouseleave', function() { animate(cloud, current); }); // (3) 当我们鼠标点击,就把当前位置做为目标值 lis[i].addEventListener('click', function() { current = this.offsetLeft; }); } }) 1.3. 触屏事件1.3.1. 触屏事件概述移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android和 IOS 都有。touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。常见的触屏事件如下:1.3.2. 触摸事件对象(TouchEvent)TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等touchstart、touchmove、touchend 三个事件都会各自有事件对象。触摸事件对象重点我们看三个常见对象列表:因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes1.3.3.案例:移动端拖动元素 touchstart、touchmove、touchend可以实现拖动元素 但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageX 和 pageY 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离 手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置拖动元素三步曲:(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子(3) 离开手指 touchend:注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 e.preventDefault();
2022年05月21日
46 阅读
0 评论
0 点赞
2022-05-17
Web APIs 练习操作
1.1. 元素偏移量 offset 系列1.1.1 offset 概述offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。 获得元素距离带有定位父元素的位置 获得元素自身的大小(宽度高度) 注意:返回的数值都不带单位 1.1.2 offset 与 style 区别offset offset 可以得到任意样式表中的样式值 offset 系列获得的数值是没有单位的 offsetWidth 包含padding+border+width offsetWidth 等属性是只读属性,只能获取不能赋值 所以,我们想要获取元素大小位置,用offset更合适 style style 只能得到行内样式表中的样式值 style.width 获得的是带有单位的字符串 style.width 获得不包含padding和border 的值 style.width 是可读写属性,可以获取也可以赋值 所以,我们想要给元素更改值,则需要用style改变 因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes1.1.3 案例:获取鼠标在盒子内的坐标 我们在盒子内点击,想要得到鼠标距离盒子左右的距离。 首先得到鼠标在页面中的坐标(e.pageX, e.pageY) 其次得到盒子在页面中的距离 ( box.offsetLeft, box.offsetTop) 用鼠标距离页面的坐标减去盒子在页面中的距离,得到 鼠标在盒子内的坐标 如果想要移动一下鼠标,就要获取最新的坐标,使用鼠标移动 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; }) 1.1.4 案例:模态框拖拽弹出框,我们也称为模态框。 1.点击弹出层,会弹出模态框, 并且显示灰色半透明的遮挡层。 2.点击关闭按钮,可以关闭模态框,并且同时关闭灰色半透明遮挡层。 3.鼠标放到模态框最上面一行,可以按住鼠标拖拽模态框在页面中移动。 4.鼠标松开,可以停止拖动模态框移动1.1.5. 案例分析: 点击弹出层, 模态框和遮挡层就会显示出来 display:block; 点击关闭按钮,模态框和遮挡层就会隐藏起来 display:none; 在页面中拖拽的原理:鼠标按下并且移动, 之后松开鼠标 触发事件是鼠标按下mousedown,鼠标移动mousemove 鼠标松开 mouseup 拖拽过程: 鼠标移动过程中,获得最新的值赋值给模态框的left和top值,这样模态框可以跟着鼠标走了 鼠标按下触发的事件源是最上面一行,就是 id 为 title 鼠标的坐标减去 鼠标在盒子内的坐标, 才是模态框真正的位置。 鼠标按下,我们要得到鼠标在盒子的坐标。 鼠标移动,就让模态框的坐标 设置为 :鼠标坐标 减去盒子坐标即可,注意移动事件写到按下事件里面。 鼠标松开,就停止拖拽,就是可以让鼠标移动事件解除 // 1. 获取元素 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'); // 2. 点击弹出层这个链接 link 让mask 和login 显示出来 link.addEventListener('click', function() { mask.style.display = 'block'; login.style.display = 'block'; }) // 3. 点击 closeBtn 就隐藏 mask 和 login closeBtn.addEventListener('click', function() { mask.style.display = 'none'; login.style.display = 'none'; }) // 4. 开始拖拽 // (1) 当我们鼠标按下, 就获得鼠标在盒子内的坐标 title.addEventListener('mousedown', function(e) { var x = e.pageX - login.offsetLeft; var y = e.pageY - login.offsetTop; // (2) 鼠标移动的时候,把鼠标在页面中的坐标,减去 鼠标在盒子内的坐标就是模态框的left和top值 document.addEventListener('mousemove', move) function move(e) { login.style.left = e.pageX - x + 'px'; login.style.top = e.pageY - y + 'px'; } // (3) 鼠标弹起,就让鼠标移动事件移除 document.addEventListener('mouseup', function() { document.removeEventListener('mousemove', move); }) }) 1.1.6 案例:仿京东放大镜 整个案例可以分为三个功能模块 鼠标经过小图片盒子, 黄色的遮挡层 和 大图片盒子显示,离开隐藏2个盒子功能 黄色的遮挡层跟随鼠标功能。 移动黄色遮挡层,大图片跟随移动功能。 1.1.7. 案例分析: 黄色的遮挡层跟随鼠标功能。 把鼠标坐标给遮挡层不合适。因为遮挡层坐标以父盒子为准。 首先是获得鼠标在盒子的坐标。 之后把数值给遮挡层做为left 和top值。 此时用到鼠标移动事件,但是还是在小图片盒子内移动。 发现,遮挡层位置不对,需要再减去盒子自身高度和宽度的一半。 遮挡层不能超出小图片盒子范围。 如果小于零,就把坐标设置为0 如果大于遮挡层最大的移动距离,就把坐标设置为最大的移动距离 遮挡层的最大移动距离:小图片盒子宽度 减去 遮挡层盒子宽度 window.addEventListener('load', function() { var preview_img = document.querySelector('.preview_img'); var mask = document.querySelector('.mask'); var big = document.querySelector('.big'); // 1. 当我们鼠标经过 preview_img 就显示和隐藏 mask 遮挡层 和 big 大盒子 preview_img.addEventListener('mouseover', function() { mask.style.display = 'block'; big.style.display = 'block'; }) preview_img.addEventListener('mouseout', function() { mask.style.display = 'none'; big.style.display = 'none'; }) // 2. 鼠标移动的时候,让黄色的盒子跟着鼠标来走 preview_img.addEventListener('mousemove', function(e) { // (1). 先计算出鼠标在盒子内的坐标 var x = e.pageX - this.offsetLeft; var y = e.pageY - this.offsetTop; // console.log(x, y); // (2) 减去盒子高度 300的一半 是 150 就是我们mask 的最终 left 和top值了 // (3) 我们mask 移动的距离 var maskX = x - mask.offsetWidth / 2; var maskY = y - mask.offsetHeight / 2; // (4) 如果x 坐标小于了0 就让他停在0 的位置 // 遮挡层的最大移动距离 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'; // 3. 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离 // 大图 var bigIMg = document.querySelector('.bigImg'); // 大图片最大移动距离 var bigMax = bigIMg.offsetWidth - big.offsetWidth; // 大图片的移动距离 X Y var bigX = maskX * bigMax / maskMax; var bigY = maskY * bigMax / maskMax; bigIMg.style.left = -bigX + 'px'; bigIMg.style.top = -bigY + 'px'; }) }) 1.2. 元素可视区 client 系列1.2.1 client概述client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。1.2.2. 淘宝 flexible.js 源码分析立即执行函数 (function(){})() 或者 (function(){}())主要作用: 创建一个独立的作用域。 避免了命名冲突问题下面三种情况都会刷新页面都会触发 load 事件。1.a标签的超链接2.F5或者刷新按钮(强制刷新)3.前进后退按钮但是 火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。所以此时后退按钮不能刷新页面。此时可以使用 pageshow事件来触发。,这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件注意这个事件给window添加。1.3.元素滚动 scroll 系列1.3.1. scroll 概述scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。1.3.2. 页面被卷去的头部如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件。1.3.3.案例:仿淘宝固定右侧侧边栏 原先侧边栏是绝对定位 当页面滚动到一定位置,侧边栏改为固定定位 页面继续滚动,会让 返回顶部显示出来 1.3.4.案例分析: 需要用到页面滚动事件 scroll 因为是页面滚动,所以事件源是document 滚动到某个位置,就是判断页面被卷去的上部值。 页面被卷去的头部:可以通过window.pageYOffset 获得 如果是被卷去的左侧window.pageXOffset 注意,元素被卷去的头部是element.scrollTop , 如果是页面被卷去的头部 则是 window.pageYOffset 其实这个值 可以通过盒子的 offsetTop可以得到,如果大于等于这个值,就可以让盒子固定定位了 //1. 获取元素 var sliderbar = document.querySelector('.slider-bar'); var banner = document.querySelector('.banner'); // banner.offestTop 就是被卷去头部的大小 一定要写到滚动的外面 var bannerTop = banner.offsetTop // 当我们侧边栏固定定位之后应该变化的数值 var sliderbarTop = sliderbar.offsetTop - bannerTop; // 获取main 主体元素 var main = document.querySelector('.main'); var goBack = document.querySelector('.goBack'); var mainTop = main.offsetTop; // 2. 页面滚动事件 scroll document.addEventListener('scroll', function() { // console.log(11); // window.pageYOffset 页面被卷去的头部 // console.log(window.pageYOffset); // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位 if (window.pageYOffset >= bannerTop) { sliderbar.style.position = 'fixed'; sliderbar.style.top = sliderbarTop + 'px'; } else { sliderbar.style.position = 'absolute'; sliderbar.style.top = '300px'; } // 4. 当我们页面滚动到main盒子,就显示 goback模块 if (window.pageYOffset >= mainTop) { goBack.style.display = 'block'; } else { goBack.style.display = 'none'; } }) 1.3.5.页面被卷去的头部兼容性解决方案需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法: 声明了 DTD,使用 document.documentElement.scrollTop 未声明 DTD,使用 document.body.scrollTop 新方法 window.pageYOffset和 window.pageXOffset,IE9 开始支持 function getScroll() { return { left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0, top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0 }; } 使用的时候 getScroll().left 1.4. 三大系列总结他们主要用法:1.offset系列 经常用于获得元素位置 offsetLeft offsetTop2.client经常用于获取元素大小 clientWidth clientHeight3.scroll 经常用于获取滚动距离 scrollTop scrollLeft 4.注意页面滚动的距离通过 window.pageXOffset 获得1.5. mouseenter 和mouseover的区别 当鼠标移动到元素上时就会触发mouseenter 事件 类似 mouseover,它们两者之间的差别是 mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发 之所以这样,就是因为mouseenter不会冒泡 跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡 1.6. 动画函数封装1.6.1. 动画实现原理核心原理:通过定时器 setInterval() 不断移动盒子位置。实现步骤: 获得盒子当前位置 让盒子在当前位置加上1个移动距离 利用定时器不断重复这个操作 加一个结束定时器的条件 注意此元素需要添加定位,才能使用element.style.left 1.6.2. 动画函数给不同元素记录不同定时器如果多个元素都使用这个动画函数,每次都要var 声明定时器。我们可以给不同的元素使用不同的定时器(自己专门用自己的定时器)。核心原理:利用 JS 是一门动态语言,可以很方便的给当前对象添加属性。 function animate(obj, target) { // 当我们不断的点击按钮,这个元素的速度会越来越快,因为开启了太多的定时器 // 解决方案就是 让我们元素只有一个定时器执行 // 先清除以前的定时器,只保留当前的一个定时器执行 clearInterval(obj.timer); obj.timer = setInterval(function() { if (obj.offsetLeft >= target) { // 停止动画 本质是停止定时器 clearInterval(obj.timer); } obj.style.left = obj.offsetLeft + 1 + 'px'; }, 30); }
2022年05月17日
36 阅读
0 评论
0 点赞
1
...
5
6
7
...
10