移动互联笔记
css动画
动画原理及 2D转换
动画原理:
连续的播放多张照片,形成一个连贯的动画
css代码中如何理解动画
一个元素存在多个css状态,连续的播放元素的这些css状态,将形成一组动画。这就是css动画
例如:
/* 假设一个元素存在以下几个状态 */
/* 状态1 */
.box {
position: relative;
left: 0
}
/* 状态2 */
.box {
position: relative;
left: 100px;
}
/* 状态3 */
.box {
position: relative;
left: 200px;
}
/* 我们将上述几种状态从上至下依序给元素赋值,将形成一个让元素从左至右移动的动画 */
帧 frame
什么是帧?上述内容中讲到的被播放的一个状态,为一帧
帧率 fps(frame per second)
描述动画在1s钟之内播放多少个状态
transform变换
在大部分的游戏引擎,3D图形引擎中,都有 transform
transform 用于描绘物体的 位置 旋转(rotate) 缩放(scale)
transform 变换,始终是参考当前状态进行变换
- translate 平移
/* translate 平移 */
/* 第一个参数: x轴的平移量,正方向向右 */
/* 第二个参数: y轴的平移量,正方向向下 */
transform: translate(100px, 200px);
/* 填一个参数的话,只有横坐标进行了平移 */
transform: translate(100px);
/* translateX x轴上平移 */
/* transform: translateX(300px);
transform: translateY(300px);
- rotate旋转
参数是
角度单位:deg
弧度单位:rad
元素的旋转角度是参考元素的正上方
顺时针为正方向
旋转中心默认为元素中心
更改元素的旋转中心:transform-origin
transform: rotate(30deg);
transform: rotateX(30deg);
transform: rotateY(30deg);
左右镜像
transform: rotateY(180deg);
上下镜像
transform: rotateX(180deg);
- scale缩放
参考代表的是缩放比例,值为1,为原始大小
transform: scale(1, 0.5);
参数只写一个,代表的是水平和竖直方向和垂直方向缩放相同的比例
transform: scale(3);
负数代表的是在指定轴上的镜像的镜像
transform: scale(-0.5, 1);
transform: scaleX(2);
transform: scaleY2);
transform: scaleZ(2);
隐藏元素的方法,将缩放调为0
transform: scale(0);
- skew 倾斜
第一个参数:水平轴的倾斜度数
第二个参数:竖直轴的倾斜度数
只设置一个参数,则只有水平轴倾斜有效
transform: skew(0, 30deg);
- transform 同时赋值多个值
/* 同时设置多个变换属性,用空格隔开 */
transform: rotate(180deg) translateX(100px) scale(0.5);
transform 的实现原理类似于矩阵,先旋转后平移和先平移后旋转结果是不同的 */
transform: translateX(100px) rotate(180deg) scale(0.5);
transform变换原点
原点的位置会影响旋转的圆心,缩放的位置
值的带选项:center, top, bottom, left, right, 还可以写具体的长度值
transform-origin: right bottom;
原点若x值和y值都为0,代表的是元素左上角的位置
第一参数值代表横向移动的距离,正数代表向右移动,负数代表向左移动
第二参数值代表纵向移动的距离,正数代表向下移动,负数代表向上移动
原点变换还会影响缩放的位置
transform-origin: left top;
transform: scaleY(1);
过渡动画
transition过渡
transition是过渡的意思,将会时元素从一个状态用动画形式过渡到零一个状态
参考:
https://developer.mozilla.org/zh-CN/docs/Web/CSS/transition
过渡属性:
transition-duration 补间动画的时长
transition-timing-function 补间动画的速度曲线
transition-delay延迟播放动画的时间
使用class来控制动画
<body>
<div class="box"></div>
<button>播放</button>
</body>
<script>
let btn = document.querySelector('button')
let box = document.querySelector('.box')
btn.addEventListener('click', () => {
if (box.classList.contains('active')) {
// 包含active
box.classList.remove('active')
} else {
box.classList.add('active')
}
})
</script>
<style>
.box {
width: 100px;
height: 100px;
background-color: #f00;
/* 初始状态 */
transform: translateX(0px);
}
/* 通过添加 active 类名来开启动画 */
.box.active {
transition: all 1s;
/* 结束状态 */
transform: translateX(200px);
}
transition事件
过渡动画播放完成事件 --transitionend
过渡动画开始播放事件 --transitionstart
过渡动画运行事件 --transitionrun
3d变换和animation动画
animate动画
animate动画
animate和transition的区别
transition 是通过属性变化,而产生过渡状态,而由浏览器自动创建补间动画 */
transition 在浏览器上动态修改css属性后,依然可以播放过渡动画 */
animation的状态是事先定义好的,后续改变css属性也不会播放新的动画 */
animation的动画状态不止起始状态和结尾状态,可以定义多个状态 */
animation动画属性
animation-name动画名称
animation-duration动画播放时长
animation-timing-function动画播放的速度曲线
animation-delay延迟播放的时间
animation-iteration-count动画播放次数
animation-direction动画播放方向
animation-fill-mode播放结束后,元素的最终状态模式
复合属性 赋值顺序
/* duration | timing-function | delay | iteration-count | direction | fill-mode | play-state | name
duration | timing-function | delay | name
duration | name
关键帧:动画播放的几个关键状态
@keyframes move {
/* 起始状态 */
/* from { */
0% {
transform: translateX(0);
color: #000;
font-size: 16px;
}
/* 用百分比定义中间状态 */
50% {
transform: translateX(1000px);
color: pink;
font-size: 32px;
}
/* 结束状态 */
/* to { */
100% {
transform: translateX(500px);
color: green;
font-size: 64px;
}
}
使用class来控制animation动画
.box {
width: 100px;
height: 100px;
background-color: #f00;
}
.box.active {
animation: move 2s linear infinite;
}
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(400px);
}
}
<div class="box"></div>
<button>播放</button>
const btn = document.querySelector('button')
const box = document.querySelector('.box')
btn.addEventListener('click', () => {
if (box.classList.contains('active')) {
box.classList.remove('active')
} else {
box.classList.add('active')
}
})
暂停和继续播放动画
animationPlayState动画播放状态
<div class="box"></div>
<button>播放</button>
const btn = document.querySelector('button')
const box = document.querySelector('.box')
btn.addEventListener('click', () => {
if (!box.classList.contains('active')) {
box.classList.add('active')
btn.textContent = '暂停'
return
}
// 读取动画播放状态 animationPlayState
console.log(getComputedStyle(box).animationPlayState)
// running 代表正在播放
if (getComputedStyle(box).animationPlayState === 'running') {
// 在播放就暂停动画
box.style.animationPlayState = 'paused'
btn.textContent = '继续'
} else {
// 若已暂停动画,则重新设置为running
box.style.animationPlayState = 'running'
btn.textContent = '暂停'
}
})
.box {
width: 100px;
height: 100px;
background-color: #f00;
}
.box.active {
animation: move 4s linear infinite alternate;
}
@keyframes move {
from {
transform: translateX(0);
}
to {
transform: translateX(400px);
}
}
动画事件
动画播放完成事件 animationend
动画播放完成事件 animationstart
动画播放完成事件 animationiteration
3d变换
步骤:
1. 搭建3d场景,在父元素上设置:transform-style: preserve-3d;
2. 在父元素上设置透视距离:perspective: 100px;
3. 给场景添加演员,给场景元素添加子元素
4. 在子元素上使用3d变换
3d立方体
Document
body {
margin: 0;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.cube {
width: 200px;
height: 200px;
/* border: 5px solid #f00; */
/* 开启3d变换 */
transform-style: preserve-3d;
/* 设置灭点 */
perspective: 500px;
/* 透视原点(灭点的坐标) */
perspective-origin: -100px center;
position: relative;
}
.face {
width: 200px;
height: 200px;
background-color: pink;
opacity: 0.3;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-weight: bolder;
font-size: 32px;
position: absolute;
top: 0;
left: 0;
}
.front {
transform: translateZ(100px);
background-color: #000;
}
.back {
transform: translateZ(-100px) rotateY(180deg);
background-color: #ff0;
}
.left {
transform: translateX(-100px) rotateY(-90deg);
background-color: #f00;
}
.right {
transform: translateX(100px) rotateY(90deg);
background-color: #0f0;
}
.up {
transform: translateY(-100px) rotateX(90deg);
background-color: #00f;
}
.down {
transform: translateY(100px) rotateX(-90deg);
background-color: #0ff;
}
</style>
</head>
<body>
<div class="cube">
<div class="face front">前</div>
<div class="face back">后</div>
<div class="face left">左</div>
<div class="face right">右</div>
<div class="face up">上</div>
<div class="face down">下</div>
</div>
</body>
</html>
渐变色分栏布局与响应式
属性选择器补充
匹配属性值包含指定内容的元素
input[name*=e] {
border: 3px solid red;
}
匹配属性值以指定内容开头的元素
input[name^=s] {
font-size: 24px;
}
匹配属性值以指定内容结束的元素
input[name$=e] {
background-color: greenyellow;
}
匹配属性值包含 dep- 开头的元素
input[name|=dep] {
border: 3px solid blue;
}
伪类选择器补充
/* 表单元素被禁用时的样式 */
input:disabled {
border: 3px solid red;
}
/* disabled仅限于表单元素使用 */
.box:disabled {
border: 3px solid black;
}
.box {
width: 200px;
height: 100px;
}
/* checked代表被勾选的元素 */
input[name=sex]:checked {
width: 100px;
height: 100px;
}
/* 选中元素中的第一个字符 */
.hw::first-letter {
color: red;
}
.p1 {
width: 100px;
height: 130px;
word-wrap: break-word;
}
/* 元素中的第一行内容 */
.p1::first-line {
color: green;
}
/* 选中的内容 */
.p2::selection {
background-color: gold;
}
渐变色
参考:https://developer.mozilla.org/zh-CN/docs/Web/CSS/gradient
线性渐变色
/* 注意:渐变色的值,适用于 background-image 属性,而不是 background-color */
/* linear-gradient
第一个参数: 渐变的方向,也可以是个角度值
默认方向为: to bottom
后续颜色参数可以追加一个距离值
*/
/* background-image: linear-gradient(to right, red, yellow 100px, green 200px); */
background-image: linear-gradient(30deg, red, red 100px, blue 100px, blue 200px, black 200px);
径向渐变
background-image: radial-gradient(red, yellow);
background-image: radial-gradient(red, red 100px, yellow 100px, yellow 200px, black 200px);
重复线性渐变
background-image: repeating-linear-gradient(to right, black, black 50px, white 50px, white 100px);
重复径向渐变
background-image: repeating-radial-gradient(red, red 50px, yellow 50px, yellow 100px);
分栏布局
.p {
width: 800px;
height: 500px;
/* 设置文本列数 */
column-count: 3;
/* 设置列宽度 */
column-width: 256px;
/* 分栏边框,属性值和边框的属性值相同 */
column-rule: 5px double red;
/* 两列文本中间的间距 */
column-gap: 100px;
word-break: keep-all;
}
响应式布局
参考地址:https://developer.mozilla.org/zh-CN/docs/Web/CSS/@media */ https://developer.mozilla.org/zh-CN/docs/Web/CSS/Media_Queries/Using_media_queries
响应式是什么
当某个事情发生后,立即做出反应就叫响应式
(此处我们讨论的是页面的响应式)
如何实现响应式?
使用媒体查询,来实现响应式布局
媒体查询:媒体查询的作用:当媒体查询条件成立时,将应用花括号中代码块的样式
语法:@media media-type and (condition1) and (condition2)
媒体类型 media-type:
备选项
all: 所有设备
print:打印机的预览模式
screen:显示屏
speech:语音合成器
举例
@media screen and (min-width: 700px) {
.box {
background-color: #f00 !important;
}
}
/* max-width: 屏幕最大宽度 */
@media screen and (max-width: 1000px) {
.box {
background-color: #00f !important;
}
}
jQuery
jquery的基础用法
jquery 简介
英文官网: https://jquery.com/
中文简单教学(支持文档查询):https://www.jquery123.com/
什么是jquery
jquery是一个js的工具库
jquery的作用
jquery有以下主要功能:
针对页面元素,jquery提供了很多操纵页面元素的函数
还有一些工具函数,方便一些运算
jquery可以充当发起网络请求的客户端工具
安装
在官网上点击download进行下载
cdn: 共享资源节点
npm 安装
```shell
npm init
npm i jquery
# 安装完后从 node_modules/jquery/dist 文件夹中获取资源
```
在页面中的使用
<!DOCTYPE html>
<html>
<head></head>
<body></body>
<!-- 通过脚本形式进行引入 -->
<script src="./jquery.min.js"></script>
<script>
// 在引入jquery后,此处就可以使用jquery的功能了
</script>
</html>
创建一个jquery对象
- 方法一:使用选择器调用$()函数直接获得jquery对象
let $box = $('.box')
console.log($box);
- 方法二:可以通过dom对象获取jquery对象
let box = document.querySelector('.box')
$box = $(box)
console.log($box);
eq和get
eq() 该函数将返回对应索引位置的jquery对象
通过函数get来获取对应索引的dom对象
// 查询一个jquery对象
let $boxs = $('.box')
console.log($boxs);
// eq() 该函数将返回对应索引位置的jquery对象
let $box = $boxs.eq(0)
console.log($box);
// 用索引获取到的是dom对象
let box = $boxs[0]
console.log(box);
// jquery对象类似一个数组,可以获取上述jquery对象的dom对象,如下:
// 也可以通过函数get来获取,
box = $boxs.get(0) // 与 $boxs[0]等价
console.log(box);
创建并插入元素
append
prepend
在元素内追加另一个元素
$body.append($box) // body追加个子元素在最后
$body.prepend($box) //body追加个子元素在前面
appendTo
prependTo
将一个元素插入另一个元素的子节点中
$box.appendTo($body)
$box.prependTo($body)
after
before
在一个元素的后面或前面添加另一个元素
$box2.after($box) //box2的后面添加一个box
$box2.before($box)
insertAfter
insertBefore
一个元素被插入到另一个元素的前或后面
$box.insertAfter($box2) //box被插在box的后面
$box.insertBefore($box2)
设置样式
使用.css('属性名','属性值')来设置
参考:https://www.jquery123.com/css/
let $boxs = $('.box')
// 使用css函数来设置和读取样式
// 读取样式
console.log($boxs.css('width'));
console.log($boxs.css('height'));
// 读取多个样式
console.log($boxs.css(['width', 'height']));
// 设置样式
// $boxs.css('background-color', '#f00')
// 通过函数设置样式
$boxs.css('background-color', function (index, value) {
// 当前成员的索引
console.log(index);
// 当前元素的对应属性值
console.log(value);
// 返回一个新的属性值
return index % 2 === 0? '#0f0': '#00f'
})
设置类
添加类 .addClass
删除类 .removeClass
判断是否存在某个类 .hasClass
开关类 .toggleClass
let $boxs = $('.box')
// 添加类
$boxs.addClass('my-box')
// 删除类
$boxs.removeClass('active')
// 判断是否存在某个类
console.log($boxs.hasClass('box'));
console.log($boxs.hasClass('active'));
// 开关类
$boxs.toggleClass('box2')
设置属性
读取自定义属性 .attr('自定义属性名')
设置属性值值 .attr('属性名','属性值')
删除属性值 .removeAttr('属性名')
let $boxs = $('.box')
// 读取自定义属性
// console.log(document.querySelector('.box').getAttribute('my-data'));
console.log($boxs.attr('my-data'));
// 设置属性值
$boxs.attr('src', './img/1.png')
// 删除属性值
$boxs.removeAttr('class')
设置标签体
读取和设置标签体 .text .html
let $p = $('.p')
// 通过jquery读取和设置标签体
// 读取标签体
console.log($p.text()); // 相当于 innerText
console.log($p.html()); // 相当于 innerHTML
// 设置标签体
$p.text(' 0 ')
$p.html(' 0 ')
绑定事件
直接调用事件名称对应的函数
使用on绑定事件
使用off解绑事件
使用one函数,绑定只触发一次的事件
let $box = $('.box')
// 给jquery对象绑定事件时,直接调用该事件名称对应的函数
// $box.click(function (ev) {
// console.log(ev);
// console.log('click');
// })
// $box.mousemove(function (ev) {
// console.log(ev);
// console.log('move');
// })
// $box.mouseenter(function (ev) {
// console.log('enter');
// })
// 手动触发事件
// $box.click()
// $box.mouseenter()
// 使用 on 函数绑定事件
const click1 = function (ev) {
console.log('click1');
console.log(ev);
}
$box.on('click', click1)
$box.on('click', function (ev) {
console.log('click2');
console.log(ev);
})
// 使用 off 解绑事件
// 如果只写一个参数,会解绑所有的对应事件
// $box.off('click')
$box.off('click', click1)
// 使用 one 函数,可以让绑定的事件只触发一次
// $box.one('click', (ev) => {
// console.log('one click');
// console.log(ev);
// })
$box[0].addEventListener('click', function () {
console.log('one click');
$box[0].removeEventListener('click', arguments.callee)
})
// 页面加载完成事件
$(function () {
console.log('load');
})
遍历元素
参考:https://www.jquery123.com/each/
使用.each遍历元素
const $lis = $('ul>li')
$lis.each(function (index, element) {
// 每个成员索引
console.log(index);
// 每个dom对象
console.log(element);
$(element).css('background-color', index % 2 === 0 ? '#f00' : '#0f0')
})
表单元素取值赋值
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form>
<div><label>姓名: <input name="name" /></label></div>
<div>性别:
<label><input type="radio" name="sex" value="male" />男</label>
<label><input type="radio" name="sex" value="female" />女</label>
<label><input type="radio" name="sex" value="other" checked />其他</label>
</div>
<div>爱好:
<label><input type="checkbox" name="hobbies" value="dlq" />打篮球</label>
<label><input type="checkbox" name="hobbies" value="tzq" />踢足球</label>
<label><input type="checkbox" name="hobbies" value="ymq" />羽毛球</label>
</div>
<div><label>部门:
<select name="dep">
<option value="" selected disabled>请选择</option>
<option value="it">IT部</option>
<option value="hr">人事部</option>
<option value="ec">行政部</option>
</select>
</label></div>
<div>
<label>简介:<textarea name="desc"></textarea></label>
</div>
<button>提交</button>
</form>
</body>
<script src="./js/jquery.js"></script>
<script>
const $btn = $('form button')
const $name = $('input[name=name]')
const $sexs = $('input[name=sex]')
const $dep = $('select[name=dep]')
const $desc = $('textarea[name=desc]')
const $hobbies = $('input[name=hobbies]')
// 用于回显的数据
let user = {
name: '李四',
sex: 'female',
hobbies: ['ymq', 'tzq'],
dep: 'hr',
desc: '这是一个\n英雄的母亲'
}
// 声明一个变量来保存选项值
let sex = 'other'
// 用来保存选项的数组
let hobbies = []
// 绑定元素变化事件
$sexs.on('change', ev => {
console.log(ev);
// ev.target 触发事件的dom对象
// 保存当前选择的元素值
sex = ev.target.value
})
$hobbies.on('change', ev => {
console.log(ev);
// ev.target 触发事件的dom对象
// 通过 checked 属性来判断是否勾选元素
console.log(ev.target.checked);
if (ev.target.checked) {
// 判断数组中是否存在选项值
if (!hobbies.includes(ev.target.value)) hobbies.push(ev.target.value)
} else {
// 判断数组中是否存在选项值,存在就删除掉
let i = hobbies.findIndex(hobby => hobby === ev.target.value)
if (i !== -1) hobbies.splice(i, 1)
}
})
// 点击事件
$btn.click(function (ev) {
// 屏蔽默认事件行为
ev.preventDefault()
// 读值
console.log($name.val());
console.log(sex);
console.log($dep.val())
console.log($desc.val());
console.log(hobbies);
})
$(function () {
// 数据回显
$name.val(user.name)
// 回显性别
sex = user.sex
$sexs.each((index, element) => {
if (element.value === sex) element.checked = true
})
// 回显爱好
hobbies = user.hobbies
$hobbies.each((index, element) => {
// 判断数组中是否包含当前元素的value值
if (hobbies.includes(element.value)) element.checked = true
})
$dep.val(user.dep)
$desc.val(user.desc)
})
</script>
</html>
特效
渐变动画
元素渐入 fadeIn
元素渐出 fadeOut
元素渐变到指定透明度 fadeTo
开关渐变 fadeToggle
滑动动画
滑下 slideDown
滑上 slideUp
滑动开关 slideToggle
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: red;
}
</style>
</head>
<body>
<h1>基本动画</h1>
<div class="section1">
<button>显示</button>
<button>隐藏</button>
<button>开关</button>
</div>
<div class="box box1"></div>
<h2>自定义动画</h2>
<div class="section2">
<button>animate</button>
</div>
<div class="box box2"></div>
<h1>渐变</h1>
<div class="section3">
<button>渐入</button>
<button>渐出</button>
<button>设置透明度</button>
<button>开关渐入渐出</button>
</div>
<div class="box box3"></div>
<h1>滑动</h1>
<div class="section4">
<button>滑下</button>
<button>滑上</button>
<button>滑动开关</button>
</div>
<div class="box box4"></div>
</body>
<script src="./js/jquery.js"></script>
<script>
// 参考:https://www.jquery123.com/category/effects/
let $box1 = $('.box1');
let $box2 = $('.box2');
let $box3 = $('.box3');
let $box4 = $('.box4');
// jquery 特效
// 基本动画
$('.section1 button:nth-child(1)').click(() => {
// 显示元素 原理是设置元素的display属性
$box1.show()
})
$('.section1 button:nth-child(2)').click(() => {
// 隐藏元素
$box1.hide()
})
$('.section1 button:nth-child(3)').click(() => {
// 开关元素
$box1.toggle()
})
// 自定义动画
$('.section2 button:nth-child(1)').click(() => {
// 自定义动画
// 第一个参数:要播放的动画属性,有些属性无法生效例如:transform background-color 等
// 第二个参数: 动画播放时长
// 第三个参数: 动画播放曲线
// 第四个参数: 动画播放结束回调
$box2.animate({
marginLeft: '300px',
paddingTop: '100px',
backgroundColor: 'rgb(0,0,255)'
}, 3000, 'swing', () => {
console.log('动画播放完成');
})
})
// 渐变动画
$('.section3 button:nth-child(1)').click(() => {
// 渐变函数的第一个参数,可以指定动画播放的时长,单位是毫秒
// 元素渐入
$box3.fadeIn(3000)
})
$('.section3 button:nth-child(2)').click(() => {
// 元素渐出
$box3.fadeOut(5000)
})
$('.section3 button:nth-child(3)').click(() => {
// 第一个参数:动画播放时长
// 第二个参数:目标透明度
// 渐变到指定透明度
$box3.fadeTo(3000, 0.5)
})
$('.section3 button:nth-child(4)').click(() => {
// 开关渐变
$box3.fadeToggle(3000)
})
// 滑动动画
$('.section4 button:nth-child(1)').click(() => {
// 滑下
$box4.slideDown(3000)
})
$('.section4 button:nth-child(2)').click(() => {
// 滑上
$box4.slideUp(3000)
})
$('.section4 button:nth-child(3)').click(() => {
// 滑动开关
$box4.slideToggle(2000)
})
</script>
</html>
元素包裹
wrap 给每个元素用指定的html内容包裹起来
wrapAll 将jquery对象查询出来的所有元素用指定的html内容包裹起来
ready事件
在文档准备就绪后执行
$().ready(() => {
// 该函数将在文档准备就绪后被执行
console.log('文档准备就绪')
})
$(() => {
// 这种写法等价于 $().ready()
console.log('文档准备就绪2')
})
// 窗口资源加载完成事件
window.addEventListener('load', () => {
console.log('窗口加载完成')
})
读取和设置元素大小和位置
位置
position 获得定位属性的top和left值
offset可以获得相对窗口的绝对坐标
宽高
.width和height可以直接赋值
outerWidth 和outerHeight包含边框内的宽高
innerWidth 和innerHeight包含内边距的宽高
// 位置
// position 函数可以获得定位属性的 top 和 left 的值
console.log($box.position());
console.log($container.position());
// offset 函数可以获得相对于窗口的绝对坐标
console.log($box.offset());
console.log($container.offset());
// 宽高
// 函数参数可以直接赋值
console.log($box.width());
console.log($box.height());
// $box.width(200)
// 包含边框在内的宽高
console.log($box.outerWidth());
console.log($box.outerHeight());
// $box.outerWidth(100)
// 包含内边距的宽高
console.log($box.innerWidth());
console.log($box.innerHeight());
获取和设置滚动条的位置
scrollTop scrollLeft 可以赋值
let $container = $('.container')
console.log($container.scrollTop());
console.log($container.scrollLeft());
// scrollTop 可以赋值
// $container.scrollTop(0)
ajax
// ajax (async javascript and xml)
// 异步 js 和 xml
// 异步的网络请求
$('button').click(() => {
console.log('click');
// 发起异步网络请求
$.ajax({
// 请求地址
url: 'https://www.bilibili.com',
// 请求方法
method: 'get',
// 请求成功的回调
success(res) {
console.log(res);
},
// 请求失败时的回调
error(reason) {
console.error(reason);
},
complete() {
console.log('无论请求成功还是失败,都会调用 complete里面的内容');
}
})
})
// 常见的 http client (发网络请求的工具叫做 http client)
// XMLHttpRequest
// fetch
// axios
// jquery.ajax
其他api
index 查询某个节点在集合中的索引
find 查询某个父节点的后代节点
closest 查询最近的父节点
canvas画布
canvas画布简介
官网参考:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Element/canvas
https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D
什么是canvas
canvas是浏览器提供的一个标签,该标签可以用来绘制图像。canvas我们通常称为画布
canvas基础使用方法
使用width 和 height 来做宽高,使用css样式会失真
使用步骤
查询canvas
获取上下文对象const ctx = canvas.getContext('2d')
绘制矩形
// 获取画布
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// 修改填充色
ctx.fillStyle = '#ff0'
// 绘制填充颜色的矩形
// rectangle 矩形
// 第一个和第二个参数代表矩形左上角的坐标
// 第三个和第四个参数是矩形的宽高
ctx.fillRect(100, 50, 200, 100)
// 修改镂空描边的颜色
ctx.strokeStyle = '#f00'
// 绘制镂空矩形
ctx.strokeRect(100, 300, 200, 100)
// 清空画布
ctx.clearRect(0, 0, 500, 500)
绘制文本
// 获取画布
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// 编辑字体
ctx.font = '32px 华文行楷'
ctx.fillStyle = '#f00'
// 填充文本
// 语法:ctx.fillText(text, x, y, max-width)
// text: 要渲染的文本
// x,y: 文本渲染的坐标位置
// max-width: 文本最大宽度,当大于该宽度,文本字体将自动缩小以自适应宽度
ctx.fillText('你好中国', 100, 100, 100)
ctx.strokeStyle = '#0f0'
// 镂空文本
ctx.strokeText('你好世界', 100, 300)
画线
// 获取画布
let c = document.querySelector('canvas')
let ctx = c.getContext('2d')
// 修改笔头宽度
ctx.lineWidth = 20
// 画线之前要开启路径
ctx.beginPath()
// 移动笔头
ctx.moveTo(100, 50)
// 绘制直线路径
ctx.lineTo(200, 50)
ctx.lineTo(200, 300)
// ctx.lineTo(100, 50)
// 闭合路径
ctx.closePath()
// 根据路径绘图
// 绘制镂空线
ctx.stroke()
// 绘制填充图形
// ctx.fill()
ctx.beginPath()
ctx.moveTo(250, 250)
// 绘制弧形
// 第一个和第二个参数: 圆心坐标
// 第三个参数: 圆半径
// 第四个和第五个参数: 起始弧度和结束弧度
// 0 度代表水平向右
// 正方向为顺时针
ctx.arc(250, 250, 250, Math.PI / 180 * 30, Math.PI)
ctx.closePath()
ctx.stroke()
// ctx.fill()
fillStyle和strokeStye
fillStyle 可以修改所有使用fill的函数所填充的颜色
strokeStyle 可以修改所有stroke函数的描边颜色
绘制图片
// mdn: https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/drawImage
// 获取画布
let c = document.querySelector('canvas')
let ctx = c.getContext('2d')
// 绘制图片,只能绘制html页面中的图片<img>标签,且必须是加载完成的图片
// 获取图片源
const img = document.querySelector('img')
img.addEventListener('load', () => {
// 图片加载完成后才能绘制图片
// 语法:
// void ctx.drawImage(image, dx, dy);
// void ctx.drawImage(image, dx, dy, dWidth, dHeight);
// void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
// 解释:
// image:img标签
// dx,dy: 绘制图片的坐标位置
// dWidth, dHeight: 绘制图片的宽度和高度
// sx, sy: 图片内部的坐标,用于截取图片
// sWidth, sHeight: 截取图片的宽高
// 绘制完整图片
// ctx.drawImage(img, 100, 50)
// ctx.drawImage(img, 100, 50, 200, 100)
// 绘制部分图片
ctx.drawImage(img, 70, 130, 250, 100, 100, 50, 250, 100)
})
动画函数requestAnimationFrame
const canvas = document.querySelector('canvas')
const ctx = canvas.getContext('2d')
// 动画目标: 将一个 50 x 50 的方块,以每秒 100px 的速度 向右移动
// requestAnimationFrame 请求动画的下一帧
// 参数是一帧之后的回调函数
requestAnimationFrame(render)
// 上一次动画时长
let lastTime = 0
// 上一帧到下一帧的间隔时间
let deltaTime = 0
let box = {
position: { x: 0, y: 0 },
width: 50,
height: 50,
// 速度: 每秒移动 100px
v: 100
}
// time 动画的播放总时长
function render(time) {
// 执行每一帧的逻辑
// 计算上一帧到这一帧的时间间隔
deltaTime = time - lastTime
lastTime = time
console.log(deltaTime);
// 速度 * deltaTime = 每帧的速度
box.position.x += deltaTime * 0.001 * box.v
// 画图
// 清空画布
ctx.clearRect(0, 0, 500, 500)
ctx.fillRect(box.position.x, box.position.y, box.width, box.height)
// 循环递归请求下一帧
requestAnimationFrame(render)
}
多媒体标签 swiper 和animate
<script>
const ctx1 = document.querySelector('.src').getContext('2d')
const ctx2 = document.querySelector('.target').getContext('2d')
const img = document.createElement('img')
img.src = './img/an2.jpg'
img.addEventListener('load', () => {
ctx1.drawImage(img, 0, 0, img.width, img.height, 0, 0, 361, 245)
})
const btn1 = document.querySelector('button:nth-of-type(1)')
const btn2 = document.querySelector('button:nth-of-type(2)')
const btn3 = document.querySelector('button:nth-of-type(3)')
// 灰度公式:g = r*0.299 + g*0.587 + b*0.114
// 灰度公式所获得的 g 值,赋值给每个像素点的 rgb 值,这样图像就变成了灰白图
// 计算 255 一半的值
let half = 255 * 0.5
// 黑白图
btn1.addEventListener('click', () => {
// 获取图像的像素信息
let id = ctx1.getImageData(0, 0, 361, 245)
console.log(id);
// id.data 每个像素点的信息,是一个数组
// 数组中每4个成员是一个像素的信息,代表 rgba 的参数值
for (let i = 0; i < id.data.length; i += 4) {
// 获取rgb值
let r = id.data[i]
let g = id.data[i + 1]
let b = id.data[i + 2]
// 计算灰度
let gray = r * 0.299 + g * 0.587 + b * 0.114
if (gray < half) {
// 由灰度可知,该像素更趋近于黑色
id.data[i] = 0
id.data[i + 1] = 0
id.data[i + 2] = 0
} else {
id.data[i] = 255
id.data[i + 1] = 255
id.data[i + 2] = 255
}
}
// 设置图像
ctx2.putImageData(id, 0, 0)
})
// 反转色
btn2.addEventListener('click', () => {
// 获取图像的像素信息
let id = ctx1.getImageData(0, 0, 361, 245)
console.log(id);
for (let i = 0; i < id.data.length; i += 4) {
// 获取rgb值
let r = id.data[i]
let g = id.data[i + 1]
let b = id.data[i + 2]
// 求 rgb 颜色值 相对于 255 的补值
id.data[i] = 255 - r
id.data[i + 1] = 255 - g
id.data[i + 2] = 255 - b
}
ctx2.putImageData(id, 0, 0)
})
// 灰度图
btn3.addEventListener('click', () => {
// 读取图片信息
let id = ctx1.getImageData(0, 0, 361, 245)
for (let i = 0; i < id.data.length; i += 4) {
// 获取rgb值
let r = id.data[i]
let g = id.data[i + 1]
let b = id.data[i + 2]
// 计算灰度
let gray = r * 0.299 + g * 0.587 + b * 0.114
// 赋值灰度
id.data[i] = gray
id.data[i + 1] = gray
id.data[i + 2] = gray
}
// 将 imageData 绘制到 ctx2 上
ctx2.putImageData(id, 0, 0)
})
</script>
微信小程序
小程序基础
微信小程序
申请小程序:https://mp.weixin.qq.com/
下载开发者工具:https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
微信小程序是什么?
是一个基于微信app为开发和运行环境的一个小性软件
开发流程
- 注册并填入小程序详细信息
创建项目:
- 其中AppId来自于:
编写代码
小程序项目结构
项目下的文件和文件夹的作用如下:
components: 小程序的自定义组件
images: 图片文件夹
pages: 存放页面文件的文件夹
index: 页面文件夹
index.js: 页面的js代码
index.json: 页面的配置
index.wxml: html模板文件
index.wxss: 页面的样式文件
utiles: 辅助用的js代码放的文件夹
app.js: 微信小程序的程序入口(程序入口:开始执行代码的地方)
app.json: 小程序应用程序的全局配置文件
app.wxss: 小程序的全局样式(.wxss文件是小程序的样式文件)
project.config.json: 小程序项目的配置
sitemap.json: 用于微信搜索小程序的配置文件
常用标签
参考:https://developers.weixin.qq.com/miniprogram/dev/component/
<!-- page 标签相当于 html 中的 body -->
<page></page>
<!-- view 标签相当于 html 中的 div -->
<view></view>
<!-- text 相当于 html 中的 span -->
<text></text>
<!-- image 相当于 html 中的 img -->
<image></image>
<!-- block 是一个自身不会显示的标签 -->
<block></block>
单位rem和rpx
rem
rem 是 html 中的长度单位,代表相对根节点(html)上字体的大小
rpx
rpx 是 微信wxml中的长度单位,rpx 真实大小的计算是参考的 750px 的屏幕宽度进行的
1rpx 的物理大小 = 750px 宽度屏幕上 1px 的大小
单位的文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html
总结
若希望某个元素再不同的机型下能够等比例缩放,则请使用 rpx 作为单位
若希望某个元素的大小再不同机型下保持不变,则请使用 px 作为单位
px的尺寸 = rpx下的尺寸 * (当前机型的屏幕宽 / 750)
rpx的尺寸 = px的尺寸 / (当前机型的屏幕宽 / 750)
认识页面对象模型page
官网地址:https://developers.weixin.qq.com/miniprogram/dev/reference/api/Page.html
使用Page函数包裹的整个对象是一个 page 对应的对象模型,例如:
Page({
data: {
// 数据
},
onLoad: {
// 加载完成事件
}
})
模板语法
参考:https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/
模板语法是微信规定的一套书写wxml的语法
插值
作用:用于将变量值插入页面
语法:
<!-- name 变量,定义在 js 文件的 data 中 -->
{{name}}
注意:插值运算的花括号中{{}},填写的内容其实是js表达式
循环渲染
作用:可以将数组数据循环显示到页面中
语法:
<!-- wx: 开头的写在标签头部的东西 称为指令 -->
<!-- array: 来自js data中的数组 -->
<!-- 使用 wx:for 一定要加上 wx:key,wx:key的值是array对象中的不可重复的属性 -->
<view wx:for="{{array}}" wx:key="id">
<!-- index: 是 wx:for 中隐式声明的变量,代表循环遍历array时的当前索引 -->
<!-- item: 是 wx:for 中隐式声明的变量,代表循环遍历array时的当前数组成员 -->
{{index}}: {{item}}
</view>
条件渲染
可以根据条件判断,选择性的渲染页面
语法:
<view wx:for="{{table}}" wx:key="name">
<text>{{index}}: 姓名 = {{item.name}}; 年龄 = {{item.age}}; 性别 = </text>
<!-- wx:if 指令的值为布尔表达式,为true是渲染该节点,否则不渲染 -->
<text wx:if="{{item.sex==='male'}}">男</text>
<!-- wx:if 可以和 wx:elif、wx:else 连用 -->
<text wx:elif="{{item.sex==='female'}}">女</text>
<text wx:else>其他</text>
</view>
小程序配置
文档:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
全局配置
app.json 文件中的配置是全局配置 影响所有页面
entryPagePath 小程序启动页的路径
pages 页面的路径列表
window 全局窗口的配置
tabBar 底部选项卡
debug 开启调试模式
页面配置
全局配置下,window中的所有内容,每个页面自身也可以配置
disableScroll 禁止用户上下滚动页面
usingComponents 使用组件
siteMap配置
小程序应用开发
js代码中的赋值与读值
在js代码的函数中读取属性的方法:
Page({
read() {
console.log('read')
// 函数中的 this 代表当前页面对象模型
console.log(this)
// 谁用this.data读取页面属性
console.log(this.data.table)
}
})
给页面属性赋值的方法:
Page({
write() {
// 使用this.setData函数进行赋值,参数是个json对象,需要修改哪个属性,就给该对象添加哪个属性
this.setData({title: '天龙八部'})
}
})
commonjs模块导入导出语法
模块导入导出语法的名称叫做:CommonJS
CommonJS: 是用于非浏览器导入js文件的一种语法
什么是模块
微信小程序项目中的js文件称为一个模块
导入导出
导出:一个js模块中的代码想要借给另一个模块使用 则需要使用导出语法
导入:将其他js模块的内容借给自己模块使用,则需要用到导入语法
语法
导出:
// 给 module.exports 进行赋值,赋值的内容就是导出的内容,可以是任意内容
module.exports = something
导入:
// 使用 require 进行导入,path是要导入模块的路径
let something = require(path)
阿里iconfont的使用
阿里iconfont是阿里的矢量图库,图库提供各式各样的图标
什么是矢量图
矢量图描述了绘图时所使用的坐标点,在等比例缩放图片时,图片不会变模糊。
图库使用流程
安装流程:
选择图标,加入自己的项目
下载自己项目
解压下载的zip文件
引入解压文件中的 iconfont.css 文件
使用方法:
使用 span 标签,给标签 class 加入 iconfont
在自己的项目中选择一个图标的 class
在 span 标签中加入所选 class 即可
注意:iconfont图标被当作字符使用,所以调节大小时,使用 font-size 属性
在微信小程序中使用iconfont
在 iconfont 网站的项目中点击 项目设置 设置字体类型为 Base64 然后下载项目
将 iconfont.css 改名为 iconfont.wxss
将 iconfont.wxss 放入小程序项目目录下
在小程序的 app.wxss 第一行代码加入 @import '...' (此处是iconfont.wxss文件路径)
安装好后其余使用方法和 html 中的方法一样
页面底部选项卡
配置项:https://developers.weixin.qq.com/miniprogram/dev/reference/configuration/app.html
在 app.json 中 增加 tabBar 配置就能添加选项卡
{
"tabBar": {
"selectedColor": "#444", // 选中的按钮的文本颜色
"list": [ // 按钮列表
{
"pagePath": "pages/read/read", // 跳转的页面路径
"text": "领读", // 按钮的文字描述
"iconPath": "image/tab_icon1.png", // 未选中时的图标路径
"selectedIconPath": "image/tab_icon1_active.png" // 选中时的图标路径
}
]
},
}
注意:要显示选项卡,按钮列表中,必须要有一个配置的 pagePath 属性值,是小程序的入口页面
注意:底部选项卡指定的页面不能是分包里的页面
自定义底部选项卡图标
可以在阿里适量图库下载需要的图标(一般来说是一个镂空图标和一个填充图标)
将下载后的图片复制到小程序项目目录下,然后再tabBar配置选项中配置即可
导航与页面间传参
导航:引导页面跳转到指定位置
导航方法有两种:1. 页面标签进行导航,类似 html 中的 a 标签;2. 使用js进行导航,类似于 location.href
使用 navigator 标签
文档:https://developers.weixin.qq.com/miniprogram/dev/component/navigator.html
语法:
<!-- url:要跳转到的页面路径 -->
<!-- 若要传递参数,可以在url后面增加 ?key=value 的参数 -->
<navigator url="path"></navigator>
使用 wx.navigateTo 函数
文档:https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateTo.html
语法:
function(){
wx.navigateTo({
// path: 要跳转的路径
// key=value: 要传递的参数
url: 'path?key=value'
})
}
// wx.navigateTo 跳转到某页 会新增堆栈
// wx.redirectTo 重定向到某页 不会新增堆栈
// wx.navigateBack 返回
参数的获取可以在另一个页面的 onLoad 声明周期函数中 options 变量中存放着参数
获取页面元素
文档:https://developers.weixin.qq.com/miniprogram/dev/api/wxml/wx.createSelectorQuery.html
方法:
步骤如下:
获取查询器:
let query = wx.createSelectorQuery()
使用查询器 查找节点
query.select(selector) 或者 query.selectAll(selector)
let nodeRef = query.select(selector) // nodeRef 大致可以理解为是页面元素
获取元素属性:
// fields 接收两个参数,
// 第一个参数:查询属性的配置
// 第二个参数:查询结束的回调
nodeRef.fields({
dataset: true, // 自定义属性,也就是标签上 data- 开头的属性
size: true, // 元素的宽高
rect: true, // 元素的位置 top left right bottom
properties: ['url'], // 元素的固有属性,不能查询 id class style 属性和绑定的事件属性
computedStyle: ['backgroundColor', 'fontSize'] // 查询样式
}, (res)=>{
// 该 res 就是查询的结果
console.log(res)
})
当调用完 fields 后 ,一定要执行 exec,例如:
query.exec()
查询组件
小程序组件类似html标签,可以使用css选择器进行查询
// this 是当前页面对象模型
// 通过 this.selectComponent 查询当前页中对应css选择器的组件
const checkbox = this.selectComponent('.checkbox')
自定义属性data-
和 html 一样,微信小程序的标签上也能自定属性,但是必须是以 data- 开头的属性,例如:
<!-- 此处的data-id就是一个自定义属性 -->
<text data-id="0">hello world</text>
除了使用 wx.createSelectorQuery 获取页面属性,来获取 data- 开头的自定义属性以外,还可以通过事件获取按钮上的自定义属性,例如:
<button data-id="0" bindtap="remove">删除</button>
remove 函数,如下:
function remove(event) {
// 通过事件对象event的target.dataset获取自定以属性
console.log(event.target.dataset)
}
将数据保存到小程序的全局对象中
// 获取小程序实例对象
const app = getApp()
app.globalData.x = 1 // 给小程序的全局对象赋值
// 只要不关闭小程序就能通过小程序全局对象保存参数
为什么要使用全局对象来存储数据
为了让每个页面共享相同的数据
持久化数据
什么叫持久化?持久化就是长期存储数据到磁盘中,关闭应用程序后,存储的数据还在
异步是什么?异步就是程序执行的时候不会等待或卡顿
同步是什么?同步的程序在调用的时候会等待或卡顿
异步存储
https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorage.html
wx.setStorage(Object object)
异步读取
https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.getStorage.html
wx.getStorage(Object object)
异步删除
https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.removeStorage.html
wx.removeStorage(Object object)
下面的是上述方法的同步版本
https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.getStorageSync.html
https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.setStorageSync.html
https://developers.weixin.qq.com/miniprogram/dev/api/storage/wx.removeStorageSync.html
表单组件
input
https://developers.weixin.qq.com/miniprogram/dev/component/input.html
<input name="name" type="text" value="{{name}}" bindinput="onNameInput" maxlength="5" />
radio
https://developers.weixin.qq.com/miniprogram/dev/component/radio.html
<!-- 单选按钮组 -->
<radio-group name="sex" bindchange="onSexChange">
<!-- 每个radio是一个选项,每个选项都应该有 value -->
<radio checked="{{sex === 'male'}}" value="male">男</radio>
<radio checked="{{sex === 'female'}}" value="female">女</radio>
<radio checked="{{sex === 'other'}}" value="other">其他</radio>
</radio-group>
checkbox
https://developers.weixin.qq.com/miniprogram/dev/component/checkbox.html
<checkbox-group name="hobbies" bindchange="onHobbiesChange">
<checkbox wx:for="{{hobbies}}" wx:key="value" value="{{item.value}}" checked="{{item.checked}}">{{item.text}}</checkbox>
</checkbox-group>
picker
https://developers.weixin.qq.com/miniprogram/dev/component/picker.html
<!-- 点击picker可以弹出选择框 -->
<!--
mode: 选择模式
range: 选择范围,可以是数组或对象数组
range-key: 当 range 是对象数组的时候,要显示的对象中的 key 值
value: 设置初始值
-->
<picker name="country" mode="selector" range="{{range}}" range-key="text" value="{{country}}" bindchange="onCountryChange">
<!-- 标签体中可以添加提示文本,用来提示当前选项 -->
当前选择: {{range[country].text}}
</picker>
switch
https://developers.weixin.qq.com/miniprogram/dev/component/switch.html
<switch name="agree" type="switch" checked="{{agree}}" bindchange="onAgreeChange"></switch>
textarea
https://developers.weixin.qq.com/miniprogram/dev/component/textarea.html
<textarea name="desc" auto-height="true" value="{{desc}}" bindinput="onDescInput"></textarea>
form
https://developers.weixin.qq.com/miniprogram/dev/component/form.html
<!-- bindsubmit 表单提交事件
bindreset 表单重置事件
-->
<form bindsubmit="onFormSubmit" bindreset="onFormReset">
...
<!-- 表单组件上需要添加 name 属性 -->
<label>姓名: <input name="name" type="text" value="{{name}}" bindinput="onNameInput" maxlength="5" /></label>
...
<!-- 给按钮添加 form-type 来充当表单中的提交或重置按钮 -->
<button form-type="submit">提交表单</button>
<button form-type="reset">重置表单</button>
</form>
总结
表单组件大部分都需要绑定初始值 value 属性或 checked 属性 且对应绑定以下事件 bindinput 或 bindchange 事件
若希望使用 form 组件来读取表单的值,则每个表单组件上都应该添加 name 属性,表单内需要添加 form-type="submit" 或 "reset" 的按钮,最后 form 上需要绑定 bindsubmit 和 bindreset 事件
分包
官网: https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages/basic.html#打包原则
什么是分包
将项目资源切分成多个小块的过程就是分包
为什么要使用分包
对项目的文件管理,有时需要通过分包来进行模块拆分
分包可以建立独立分包,独立分包中的页面被访问的时候,不会下载主要包和普通分包的资源,只会下载独立分包自己的资源
如何分包
首先整理需要分包的资源,如下:
├── app.js
├── app.json
├── app.wxss
├── packageA # 这是分包 名字叫 packageA
│ └── pages # 这是分包中的页面
│ ├── cat
│ └── dog
├── packageB # 这是另一个分包 名字叫 packageB
│ └── pages # 这是另一个分包中的页面
│ ├── apple
│ └── banana
| ... # 此后还可以添加任意数量的分包
├── pages
│ ├── index
│ └── logs
└── utils
然后再 app.json 中添加分包配置
// app.json
{
"pages":[
"pages/index",
"pages/logs"
],
// subpackages 是分包的配置
"subpackages": [
{
// root 对应分包文件夹路径
"root": "packageA",
// pages 对应分包的页面路径
// 注意路径的写法 此处的相对路径根是在上面 root 中配置的 packageA
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"name": "pack2",
"pages": [
"pages/apple",
"pages/banana"
]
}
// ... 此后还可以添加任意多个分包配置
]
}
屏幕适配
rpx
rpx 是 微信wxml中的长度单位,rpx 真实大小的计算是参考的 750px 的屏幕宽度进行的
1rpx 的物理大小 = 750px 宽度屏幕上 1px 的大小
单位的文档:https://developers.weixin.qq.com/miniprogram/dev/framework/view/wxss.html
## 总结
若希望某个元素再不同的机型下能够等比例缩放,则请使用 rpx 作为单位
若希望某个元素的大小再不同机型下保持不变,则请使用 px 作为单位
px的尺寸 = rpx下的尺寸 * (当前机型的屏幕宽 / 750)
rpx的尺寸 = px的尺寸 / (当前机型的屏幕宽 / 750)
wx.getWindowInfo
注意: wx.getWindowInfo 这个方法需要在较高版本的微信开发框架下才能使用,请在下图中修改微信框架版本为最新或最多人使用的版本
作用
wx.getWindowInfo 的作用主要是用来查看设备屏幕的参数,例如: 导航栏高度,底部手势条高度等等。通过这个功能我们可以用来解决自定义导航栏或自定义底部选项卡被手机的状态栏或手势条遮挡的问题
设备的本地功能调用
什么是设备的本地功能
在不同的设备上可能存在一些设备独有的功能,例如:摄像头,陀螺仪,卫星定位等等
如何使用本地功能
查找小程序文档 API,API 提供了各式各样的本地设备功能 https://developers.weixin.qq.com/miniprogram/dev/api/
例如:扫描二维码
wx.scanCode({
onlyFromCamera: false,
success: (res) => {
console.log(res);
},
});
例如:摄像头拍照
<!-- 添加相机组件到页面 -->
<camera
device-position="7"
flash="off"
binderror="error"
style="width: 100%; height: 300px;"
></camera>
// 获取相机上下文对象
let cameraCtx = wx.createCameraContext();
// 拍照
cameraCtx.takePhoto({
success: (res) => {
// res.tempImagePath 拍摄的图片路径
},
});
使用scroll-view进行下拉刷新
创建 scroll-view 如下
```html
<!--
给scroll-view添加下拉刷新功能
当高度为动态计算的高度时 一定要给高度设置初始值 例如:style="height: {{scrollHeight || 666}}px" 或者 scrollHeight 初始设置为 666
refresher-enabled 开启下拉刷新功能
refresher-default-style 下拉时三个点的颜色
refresher-background 下拉框背景色
refresher-triggered 是否触发刷新 true 为触发 false 为关闭 当设置为false时将关闭下拉刷新
bindrefresherrefresh 绑定下拉刷新事件
-->
...
页面内容写在这里
...
```
实现页面依赖的 js
```js
// js 代码的 data 和 onPullDownRefresh 如下
Page({
...
data: {
triggered: false,
scrollHeight: 1024, // 滚动区域高度
},
onPullDownRefresh() {
this.setData({triggered: true}) // 开启刷新状态
// todo 做刷新需要做的事情 这里用定时器来模拟网络请求
setTimeout(()=>{
this.setData({triggered: false}) // 只要设置 triggered 下拉刷新就会关闭
}, 3000)
}
})
```
事件
事件绑定语法:
<!-- key: bind catch capture-bind capture-catch 中的任意一个 -->
<!-- eventName: 事件名称 -->
<!-- eventHandler: 事件处理程序,值是一个字符串,代表对应js文件的方法名 -->
<button key:eventName="eventHandler">btn</button>
<!-- 例如绑定点击事件 -->
<button bind:tap="btnClick">click</button>
<!-- bind 和 catch 后面的冒号可以省略 -->
<button catchtap="btnClick">click</button>
bind
普通的绑定事件,并且事件可以冒泡
catch
绑定事件,但不会冒泡
capture-bind
捕获事件并可以冒泡
capture-catch
捕获事件并不冒泡
授权
官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html
什么是授权
用户需要使用本地设备上的一些设备功能时,需要向系统获取设备权限,例如:摄像头,定位信息,联系人,陀螺仪等
此时就需要进行授权操作
什么时候使用授权
在用户即将使用一些需要授权才能使用的本地设备功能时(微信小程序已经把这些功能制作成了接口,当需要调用这些接口的时候就可以进行授权)
流程
读取当前权限
判断是否存在权限
不存在权限就进行授权
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope
wx.getSetting({
success(res) {
if (!res.authSetting['scope.record']) {
// 调用 wx.authorize 进行授权
wx.authorize({
scope: 'scope.record',
success() {
// 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问
wx.startRecord();
},
});
}
},
});
权限列表
文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/authorize.html#scope-列表
授权期限
授权后,权限将一直存在,直到小程序被删除为止
关于二次授权
用户在进行授权时是可以拒绝的,若拒绝授权,我们可以使用 wx.openSetting 打开设置引导用户开启权限
例如以下代码
wx.getSetting({
success: (res) => {
console.log(res);
if (!res.authSetting['scope.camera']) {
if (typeof res.authSetting['scope.camera'] === 'undefined') {
// 用户没有进行过授权
wx.authorize({
scope: 'scope.camera',
success: () => {
console.log('开启摄像头');
},
});
} else {
// 用户拒绝过授权
// openSetting 打开权限设置
// 设置中包含的是已经授权或被拒绝过的权限
wx.openSetting({
success(res) {
console.log(res);
},
});
}
} else {
console.log('开启摄像头');
}
},
});
组件开发
官网: https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html
什么是组件
具备独立的 js,json,wxml,wxss的一段代码是一个组件
组件主要的作用是复用页面代码
创建组件
注册组件
组件结构
组件生命周期
https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/lifetimes.html
组件属性
// 在组件的配置中添加 properties 配置
Component({
/**
* 组件的属性列表
*/
properties: {
// 声明属性 value
// 具体声明方式可参考 https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html#properties-%E5%AE%9A%E4%B9%89
value: {
type: String, // 属性类型 可以通过设置 optionalTypes 来接收多种类型的数据
value: 'o,k', // 属性默认值
// 属性变化监听器
observer: function (newVal, oldVal) {
console.log(newVal);
console.log(oldVal);
console.log(this)
}
}
},
})
组件内设置属性的方法和 Page 设置 data 是一样的
// 使用 setData 设置属性
this.setData({value: 'hello'})
在 Page 中注册组件后就可以将组件作为标签使用了
// 注册组件
{
"useingComponent": {
"my-component": "path/to/component"
}
}
<!-- 使用组件 -->
<!-- _value 是当前页面 data 中的值 -->
<my-component value="{{_value}}"></my-component>
注意: 除了在组件内修改 value 值以外,还可以通过修改 Page 的 _value 来修改组件的 value 值
组件方法
获取组件并调用组件方法
Page({
getComponent() {
// 给组件添加 class="my-component"
// 通过 selectComponent 获取组件
let com = this.selectComponent('.my-component')
// 假设组件 methods 中有个方法叫 myFn
// 调用组件方法
com.myFn()
}
})
组件事件
<!-- 假设组件的模板如下 -->
<button bindtap="onClick">发送点击事件</button>
// 组件声明如下
Component({
methods: {
onClick() {
// triggerEvent 可以发出事件
// 文档: https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/events.html#%E7%9B%91%E5%90%AC%E4%BA%8B%E4%BB%B6
// 第一个参数: 发出的事件名称
// 第二个参数: 事件传递出去的参数
// 第三个参数: 事件选项
this.triggerEvent('my-click', 'my detail')
}
}
})
<!-- Page 的 wxml 如下 -->
<!-- 通过 bind:<event-name> 的方式绑定事件 -->
<my-component bind:my-click="myClickHandler"></my-component>
Page({
// myClickHandler 将接收组件的 my-click 事件
myClickHandler(event) {
// event.detail 的值就是 triggerEvent 的第二个参数值
console.log(event.detail)
}
})
iconfont的使用
<!-- 图标被当作字符处理,所以可以使用类似span i b u 这种处理文本的标签来显示图标 -->
<!-- 方法一: unicode 编码 -->
<!-- &开头;分号结尾的内容 称为 html 实体 -->
<!-- 图标所在的标签位置,一定要添加字体家族簇的名称 -->
<span class="iconfont" style="color: #0f0; font-size: 64px">helloworld</span>
<!-- 方法二: class类名 -->
<div>
hello<i class="iconfont icon-qianbi1"></i>world
</div>
<!-- 方法三: symbol 引入彩色图标 -->
<!-- 同样使用 font-size 来修改大小 -->
<svg style="font-size: 64px" class="icon" aria-hidden="true">
<!-- xlink:href 处添加图标名 -->
<use xlink:href="#icon-dagu"></use>
</svg>
云开发
数据的crud
查询数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<button class="btn1">查询列表</button>
<div class="page-search">
<input name="kw" placeholder="请输入姓名中包含的关键字" />
<input name="page" placeholder="请输入页码" />
<button class="btn2">分页查询</button>
</div>
</body>
<script src="../js/av-min.js"></script>
<script>
// 文档 : https://leancloud.cn/docs/leanstorage_guide-js.html#hash860317
// 初始化sdk
AV.init({
appId: "94Ddp1dFbb1BTQsKcunCQYFk-gzGzoHsz",
appKey: "te8OSHbMAvaunzdFGzzTvvvC",
// server服务器地址,来自于 设置->应用keys 中的"REST API 服务器地址"
serverURL: "https://94ddp1df.lc-cn-n1-shared.com"
})
// 按照id查询
// 创建查询对象,参数为表名称
let query = new AV.Query('Student')
// 添加查询条件
// equalTo是相等判断
// 第一个参数:要查询的表中的字段名
// 第二个参数:要查询的对应字段值
// query.equalTo('objectId', '605863124bcfb013cb35e9e9')
// // 调用 query.find 函数进行查询
// let result = query.find()
// console.log(result) // result 是promise对象,所以可以调用then和catch函数
// result.then(res => {
// // 在then函数中拿到查询结果
// // res是个数组,结果存放在数组的项的 attrbuites 中
// console.log(res)
// }).catch(reason => {
// console.error(reason)
// })
// 模糊查询
query = new AV.Query('Student')
// 调用 contains 函数查询指定字段中包含某段文本的数据
// 第一个参数:字段名称
// 第二个参数:所包含的内容
// query.contains('name', '三')
// query.find().then(res => {
// console.log(res)
// }).catch(reason => {
// console.error(reason)
// })
// 方法二:通过正则表达式进行匹配
// let regex = /^[\s\S]*三[\s\S]*$/
// // 通过 query。matches 函数,让指定字段去匹配正则表达式,查询结果会返回匹配成功的数据
// query.matches('name', regex)
// query.find().then(res => {
// console.log(res)
// }).catch(reason => {
// console.error(reason)
// })
// 条件查询
// 大于 greaterThan
// 大于等于
// query.greaterThanOrEqualTo('age', 16)
// // 小于 lessThan
// // 小于等于
// query.lessThanOrEqualTo('age', 27)
// 不等于
// query.notEqualTo('name', '张三')
// query.find().then(res => {
// console.log(res)
// }).catch(reason => {
// console.error(reason)
// })
// 列表查询
// 列表查询若要分批次查询,需要在每行数据记录时间戳,createTime 和 updateTime,分别代表创建数据的时间,和修改数据的时间
// let btn1 = document.querySelector('.btn1')
// btn1.addEventListener('click', () => {
// findNext()
// })
// // 列表查询,一定要记录最后一条数据的时间戳
// let lastTime
// // 列表查询函数
// function findNext() {
// let _query = new AV.Query('Student')
// if (lastTime)
// _query.lessThan('updateTime', lastTime)
// // 可以通过limit函数限制数据库一次查询的数量
// _query.limit(3)
// // 排序
// 按 createdAt 升序排列
// query.ascending('createdAt');
// // 按照指定字段进行降序排列
// // 参数是进行降序排列的字段名称
// _query.descending('updateTime')
// _query.find().then(res => {
// console.log(res)
// // 跟新lastTime
// lastTime = res[res.length - 1].attributes.updateTime
// }).catch(reason => {
// console.error(reason)
// })
// }
// 分页查询
// 分页查询有以下几个必要参数
// 查询的当前页
// let page = 1
// // 每页查询的记录数
// let size = 3
// // 限制查询出来的数据量
// query.limit(size)
// // 跳过前面页码中的数据
// // 参数是需要跳过的记录数
// query.skip((page - 1) * size)
// query.find().then(res => {
// console.log(res)
// }).catch(reason => {
// console.error(reason)
// })
// 分页查询函数
// kw: 代表姓名的模糊匹配
function page(page, size, kw) {
console.log(kw)
query = new AV.Query('Student')
// 增加查询条件
if (kw && kw.trim() !== '') {
// let regex = new RegExp('^[\\s\\S]*' + kw + '[\\s\\S]*$')
// query.matches('name', regex)
query.contains('name', kw)
}
// 限制查询出来的数据量
query.limit(size)
// 跳过前面页码中的数据
// 参数是需要跳过的记录数
query.skip((page - 1) * size)
query.descending('updateTime')
query.find().then(res => {
console.log(res)
}).catch(reason => {
console.error(reason)
})
// 查询满足条件的总记录数
query.count().then(count => {
console.log('student表中,满足条件的记录有:' + count + '条')
// 计算总页数
let totalPage = Math.ceil(count / size)
console.log('总共' + totalPage + '页')
})
}
let btn2 = document.querySelector('.btn2')
let inp = document.querySelector('.page-search>input[name=page]')
let kw = document.querySelector('.page-search>input[name=kw]')
btn2.addEventListener('click', () => {
page(Number(inp.value), 3, kw.value)
})
</script>
</html>
删除数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script src="../js/av-min.js"></script>
<script>
// 文档: https://leancloud.cn/docs/leanstorage_guide-js.html#hash664090316
// 初始化sdk
AV.init({
appId: "94Ddp1dFbb1BTQsKcunCQYFk-gzGzoHsz",
appKey: "te8OSHbMAvaunzdFGzzTvvvC",
serverURL: "https://94ddp1df.lc-cn-n1-shared.com"
})
// 使用 createWithoutData 创建一行数据
// 第一个参数:表名
// 第二个参数:指定表下的id值
let stu = AV.Object.createWithoutData('Student', '60596249eec6e5629f95531f')
// 调用destroy函数,销毁该行数据
let result = stu.destroy()
result.then(res => {
console.log(res)
}).catch(reason => {
console.error(reason)
})
</script>
</html>
添加数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
</style>
</head>
<body>
</body>
<script src="../js/av-min.js"></script>
<script>
// 初始化sdk
AV.init({
appId: "94Ddp1dFbb1BTQsKcunCQYFk-gzGzoHsz",
appKey: "te8OSHbMAvaunzdFGzzTvvvC",
// server服务器地址,来自于 设置->应用keys 中的"REST API 服务器地址"
serverURL: "https://94ddp1df.lc-cn-n1-shared.com"
})
// 什么是crud?crud中文含义是 增删改查
// LeanCloud数据库类型和数据格式
// LeanCloud采用非关系型数据库(每条数据存储方式,是以key:value的方式存储的数据库)
// 保存
// 文档:https://leancloud.cn/docs/leanstorage_guide-js.html#hash632314851
// 通过 AV.Object.extends() 函数创建类型(该类型对应数据库中的表)
// 其中函数参数,就是表名
const Student = AV.Object.extend('Student')
// 创建类型的实例(该实例对应数据库中的一行数据)
let student = new Student()
// 设置字段(字段指的是数据库中的列名)
student.set('name', '张三')
student.set('sex', 'male')
student.set('age', 16)
// 创建新数据时记录时间戳
let now = Date.now()
student.set('createTime', now)
student.set('updateTime', now)
// 保存对象
let result = student.save() // save函数将返回一个promise对象
console.log(result)
// 调用then函数,传入一个回调函数作为参数
// 当save执行成功后,将调用参数中的回调函数
result.then((res) => {
console.log(res)
}).catch((reason) => { // 函数在save失败调用catch函数中的回调函数
console.error(reason)
})
</script>
</html>
修改数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script src="../js/av-min.js"></script>
<script>
// 初始化sdk
AV.init({
appId: "94Ddp1dFbb1BTQsKcunCQYFk-gzGzoHsz",
appKey: "te8OSHbMAvaunzdFGzzTvvvC",
serverURL: "https://94ddp1df.lc-cn-n1-shared.com"
})
// 通过保存操作来修改数据
// 创建一行数据
// 第一个参数:数据来源的表名
// 第二个参数:表中的ObjectId值,用于指示要修改哪条数据
let stu = AV.Object.createWithoutData('Student', '605863124bcfb013cb35e9e9')
// 修改想要更新的字段
stu.set('name', '李四')
stu.set('sex', 'female')
stu.set('age', 24)
// 修改update时间戳
stu.set('updateTime', Date.now())
// 保存
stu.save().then(res => {
console.log(res)
}).catch(reason => {
console.error(reason)
})
</script>
</html>
组合查询
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script src="../js/av-min.js"></script>
<script>
// 有时需要查询同时满足两个条件的数据或者有两个条件满足其一就可以的数据
// 这是可以使用组合查询
// 方法:
// 有多少个条件就创建多少个查询对象
// 例如以下两个查询条件,分别查询书名和作者
let query1 = new AV.Query('Book')
query1.contains('name', '钢铁')
let query2 = new AV.Query('Book')
query2.contains('author', '张')
// 将多个查询条件使用组合查询并列起来
// or 或 and 且
let query = AV.Query.or(query1, query2)
// 调用万or后的query依然是个查询对象
// 可以使用query.find进行数据库查询
query.find()
</script>
</html>
小程序中使用的LeanCloud
LeanCloud简介 UCloud
云服务是什么?
云,指的是云端,也就是互联网上的远程端
云服务:远程端提供的服务,就叫云服务。
例如:百度云,阿里云,腾讯云
LeanCloud介绍
LeanCloud就云服务商家中的一家,它简单方便的提供了数据库云服务。
LeanCloud的作用
LeanCloud可以为我们提供远程服务器和数据库,我们在开发小程序时,需要使用数据库,将数据存储下来,所以我们要用到LeanCloud的数据库服务
SDK(software development kit) 是啥?
sdk是云服务平台提供的开发工具,用于用户在客户端访问云平台的云服务
下载sdk
地址:https://cdn.jsdelivr.net/npm/leancloud-storage@4.10.1/dist/av-min.js
开发流程:
安装sdk
在html当中,使用script标签直接引入
创建应用
在LeanCloud登录后台,点击创建应用按钮创建一个应用
创建好后,点击应用的存储按钮,进入控制台后,点击侧边栏中的 设置->应用keys 找到和自己应用相关的参数
初始化sdk
详见:《安装LeanCloundSDK.html》
安装sdk
文档: https://leancloud.cn/docs/sdk_setup-js.html#hash175614496
按要求,下载 av-core-min.js,并移动到小程序的 libs 目录下
下载最新版本的 adapter 适配器文件 index.js,移动到 libs 目录,并将文件重命名为 leancloud-adapters-weapp.js
验证 sdk
微信小程序管控了可以访问的网站,所以需要进行以下设置:
在开发者工具中,选择右上角的 详情-> 本地设置 -> 勾选不校验合法域名
上述设置完成后,就可以在代码中使用 LeanCloud sdk 了
结合数据库的开发流程
开发顺序:
添加数据的页面
列表页面
编辑和删除页面