首页

微信小程序之卡片层叠滑动效果

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

微信小程序之卡片层叠滑动效果

代码:
js:

// index/gun/jsSwiper2/jsSwiper2.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
    startX: 0,
    endX: 0,
    iCenter: 3,
    datas: [{
      id: 1,
      zIndex: 2,
      opacity: 0.2,
      left: 40,
      iamge: "../../images/1.jpg",
      animation: null
    },
    {
      id: 2,
      zIndex: 4,
      opacity: 0.4,
      left: 80,
      iamge: "../../images/2.jpg",
      animation: null
    },
    {
      id: 3,
      zIndex: 6,
      opacity: 0.6,
      left: 120,
      iamge: "../../images/3.jpg",
      animation: null
    },
    {
      id: 4,
      zIndex: 8,
      opacity: 1,
      left: 160,
      iamge: "../../images/4.jpg",
      animation: null
    },
    {
      id: 5,
      zIndex: 6,
      opacity: 0.6,
      left: 200,
      iamge: "../../images/5.jpg",
      animation: null
    },
    {
      id: 6,
      zIndex: 4,
      opacity: 0.4,
      left: 240,
      iamge: "../../images/6.jpg",
      animation: null
    },
    {
      id: 7,
      zIndex: 2,
      opacity: 0.2,
      left: 280,
      iamge: "../../images/7.jpg",
      animation: null
    },
    ],
    order: []
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    this.__set__();
    this.move();
  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady: function () {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow: function () {

  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide: function () {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload: function () {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh: function () {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom: function () {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  },
  move: function () {
    var datas = this.data.datas;
    /*图片分布*/
    for (var i = 0; i < datas.length; i++) {
      var data = datas[i];
      var animation = wx.createAnimation({
        duration: 200
      });
      animation.translateX(data.left).step();
      this.setData({
        ["datas[" + i + "].animation"]: animation.export(),
        ["datas[" + i + "].zIndex"]: data.zIndex,
        ["datas[" + i + "].opacity"]: data.opacity,
      })
    }
  },
  /**左箭头 */
  left: function () {
    //
    var last = this.data.datas.pop(); //获取数组的最后一个
    this.data.datas.unshift(last);//放到数组的第一个
    var orderFirst = this.data.order.shift();
    this.data.order.push(orderFirst);
    this.move();
  },
  /** */
  right: function () {
    var first = this.data.datas.shift(); //获取数组的第一个
    this.data.datas.push(first);//放到数组的最后一个位置
    var orderLast = this.data.order.pop();
    this.data.order.unshift(orderLast);
    this.move();
  },
  /**点击某项 */
  choose: function (e) {
    var that = this;
    var id = e.currentTarget.dataset.id;
    var order = that.data.order;
    var index = 0;
    for (var i = 0; i < order.length; i++) {
      if (id == order[i]) {
        index = i;
        break;
      }
    }
    if (index < that.data.iCenter) {
      for (var i = 0; i < that.data.iCenter - index; i++) {
        this.data.datas.push(this.data.datas.shift()); //获取第一个放到最后一个
        this.data.order.unshift(this.data.order.pop());
        // this.right()  
      }
    } else if (index > that.data.iCenter) {
      for (var i = 0; i < index - that.data.iCenter; i++) {
        this.data.datas.unshift(this.data.datas.pop()); //获取最后一个放到第一个
        this.data.order.push(this.data.order.shift());
        // this.left();
      }
    }
    this.move();
  },
  /**新的排列复制到新的数组中 */
  __set__: function () {
    var that = this;
    var order = that.data.order;
    var datas = that.data.datas;
    for (var i = 0; i < datas.length; i++) {
      that.setData({
        ["order[" + i + "]"]: datas[i].id
      })
    }
  },
  //手指触发开始移动
  moveStart: function (e) {
    console.log(e);
    var startX = e.changedTouches[0].pageX;
    this.setData({
      startX: startX
    });
  },
  //手指触摸后移动完成触发事件
  moveItem: function (e) {
    console.log(e);
    var that = this;
    var endX = e.changedTouches[0].pageX;
    this.setData({
      endX: endX
    });
    //计算手指触摸偏移剧距离
    var moveX = this.data.startX - this.data.endX;
    //向左移动
    if (moveX > 20) {
      this.left();
    }
    if (moveX < -20) {
      this.right();
    }
  },
})


wxml:

<view class="teachers_banner">
  <view class="container clearfix teachers_b">
    <view class="slide" id="slide" bindtouchstart='moveStart' bindtouchend='moveItem'>

      <block wx:for="{{datas}}">
        <li animation="{{item.animation}}" style="z-index: {{item.zIndex}} ;opacity:{{item.opacity}};" bindtap="choose" data-id="{{item.id}}">
          <image src="{{item.iamge}}"></image>
        </li>
      </block>

    </view>
  </view>
</view>

wxss:

.teachers_banner {
  width: 100%;
  height: 650px;
  background-size: cover;
  position: relative;
  overflow: hidden;
}

.teachers_b {
  position: relative;
  margin-top: 80px;
}

#slide {
  margin: 0 auto;
  width: 100%;
  height: 350px;
  position: relative;
}

image {
  width: 400rpx;
  height: 550rpx;
}

#slide li {
  position: absolute;
  width: 400rpx;
  display: -webkit-box;
  display: -webkit-flex;
  display: flex;
  align-items: flex-start;
  -webkit-box-align: flex-start;
  -webkit-align-items: flex-start;
  overflow: hidden;
  box-shadow: 0 0 20px #1d374d;
}

#slide li img {
  width: 100%;
  height: 100%;
}

.slide_right {
  padding: 40px;
  -webkit-box-flex: 1;
  -webkit-flex: 1;
  flex: 1;
  min-width: 0;
}

.slide_right dl {
  padding-top: 10px;
}

.arrow .prev, .arrow .next {
  position: absolute;
  width: 50px;
  top: 38%;
  z-index: 9;
  font: 700 96px 'simsun';
  opacity: 0.3;
  color: #fff;
  cursor: pointer;
}

效果:
1.左右滑动时,向相应方向移动一个卡片位置;
2.点击某一项时,将点击项位置移动到中间位置;

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务



前端 之 jQuery

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

文章目录
jQuery
jQuery的安装:
jQuery 语法
选择器
基本选择器:
层级选择器
基本筛选器
样式操作
表单筛选器
遍历方法
jQuery链式操作
jQuery 事件
$(document).ready()
click()
dblclick()
mouseenter()
mouseleave()
mousedown()
mouseup()
hover()
focus()
blur()
input()
取消标签默认的事件
事件冒泡
事件委托
jQuery自带的动画效果
jQuery
jQuery的字面意思其实就是JavaScript和查询(Query),即用于辅助开发JavaScript的库。jQuery 极大地简化了 JavaScript 编程,jQuery使用户可以更加方便地处理HTML(标准通用标记语言下的一个应用)、events、实现动画效果,而且方便地为网站提供AJAX交互。

实例找到div标签并给div标签设置为红色

原生js操作
var d1Ele = document.getElementById('d1');
d1Ele.style.color = 'red';
jQuery操作
$('#d1').css('color','blue');
1
2
3
4
5
jQuery的安装:
您可以从网页中添加 jQuery

1、从官网http://jquery.com/download/下载 jQuery 库

里面分为:Production version(用于实际的网站中,已被精简和压缩)和Development version(用于测试和开发,未压缩,是可读的代码),一般使用Production version压缩版本的。

2、从 CDN 中载入 jQuery, 如从 Google 或 百度 中加载 jQuery

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.0/jquery.min.js"></script>
或者
<script src="http://libs.baidu.com/jquery/1.10.2/jquery.min.js"></script>
或者
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.slim.js"></script>
1
2
3
4
5
一定要先导入后使用

jQuery 语法
jQuery 语法是通过选取 HTML 元素,并对选取的元素执行某些操作。

$(selector).action()
1
美元符号定义 jQuery
选择符(selector)“查询"和"查找” HTML 元素
jQuery 的 action() 执行对元素的操作
选择器
jQuery 选择器可以对 HTML 元素组或单个元素进行操作。 jQuery 选择器基于元素的 id、类、类型、属性、属性值等查找HTML 元素。它基于已经存在的 CSS 选择器外,还有一些自定义的选择器。

基本选择器:
ID选择器、类名选择器、标签选择器、组合选择器

选择器 实例 选取
* $("*") 所有元素
#id $("#d1") id=“d1” 的元素
.class $(".name") class=“name” 的所有元素
.class , .class $(".name,.password") class 为 “name” 或 “password” 的所有元素
*element $(“span”) 所有 <span> 元素
el1,el2,el3 $(“h1,div,p”) 所有 <h1>、<div> 和 <p> 元素
提出注意点,原生DOM查找和jQuery查找出来的结果区别DOM对象与jQuery对象
DOM对象与jQuery对象互相转换
$('#d1')[0]
var d1Ele = document.getElementById('d1');
$(d1Ele)
1
2
3
4
层级选择器
选择器 实例 选取
parent > child $(“div > p”) <div> 元素的直接子元素的所有 <p> 元素
parent descendant $(“div p”) <div> 元素的后代的所有 <p> 元素
element + next $(“div + p”) 每个 <div> 元素相邻的下一个 <p> 元素
element ~ siblings $(“div ~ p”) <div> 元素同级的所有 <p> 元素
基本筛选器
选择器 实例 选取
:first $(“p:first”) 第一个 <p> 元素
:last $(“p:last”) 最后一个 <p> 元素
:even $(“tr:even”) 所有偶数 <tr> 元素
:odd $(“tr:odd”) 所有奇数 <tr> 元素
:gt(nu) $(“ul li:gt(3)”) 列举 index 大于 3 的元素
:lt(nu) $(“ul li:lt(3)”) 列举 index 小于 3 的元素
:eq(nu) $(“ul li:eq(3)”) 列举 index 等于 3 的元素
:not(selector) $(“input:not(:empty)”) 所有不为空的输入元素
:has(element) $(“div:has(a)”) 返回拥有一个或多个元素在其内的所有元素
/用ul标签举例
$('#ul>li:first')
$('#u1>li:last')
$('#ul>li:eq(3)')
$('#ul>li:even')
$('#ul>li:odd')
$('#ul>li:gt(3)')
$('#ul>li:lt(3)')
$('#u1>li:not(.c1)')

最后一个has的玩法需要写个三个div:一个空div、一个儿子有a标签、一个儿子没有a,孙子有a标签
$('div:has(a)')
输出:
k.fn.init(3) [div, div#d2, div#d3, prevObject: k.fn.init(1)]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
样式操作
方法 描述
addClass() 向被选元素添加一个或多个类名
after() 在被选元素后插入内容
append() 在被选元素的结尾插入内容
appendTo() 在被选元素的结尾插入 HTML 元素
attr() 设置或返回被选元素的属性/值
before() 在被选元素前插入内容
clone() 生成被选元素的副本
css() 为被选元素设置或返回一个或多个样式属性
detach() 移除被选元素(保留数据和事件)
empty() 从被选元素移除所有子节点和内容
hasClass() 检查被选元素是否包含指定的 class 名称
height() 设置或返回被选元素的高度
html() 设置或返回被选元素的内容
innerHeight() 返回元素的高度(包含 padding,不包含 border)
innerWidth() 返回元素的宽度(包含 padding,不包含 border)
insertAfter() 在被选元素后插入 HTML 元素
insertBefore() 在被选元素前插入 HTML 元素
offset() 设置或返回被选元素的偏移坐标(相对于文档)
offsetParent() 返回第一个定位的祖先元素
outerHeight() 返回元素的高度(包含 padding 和 border)
outerWidth() 返回元素的宽度(包含 padding 和 border)
position() 返回元素的位置(相对于父元素)
prepend() 在被选元素的开头插入内容
prependTo() 在被选元素的开头插入 HTML 元素
prop() 设置或返回被选元素的属性/值
remove() 移除被选元素(包含数据和事件)
removeAttr() 从被选元素移除一个或多个属性
removeClass() 从被选元素移除一个或多个类
removeProp() 移除通过 prop() 方法设置的属性
replaceAll() 把被选元素替换为新的 HTML 元素
replaceWith() 把被选元素替换为新的内容
scrollLeft() 设置或返回被选元素的水平滚动条位置
scrollTop() 设置或返回被选元素的垂直滚动条位置
text() 设置或返回被选元素的文本内容
toggleClass() 在被选元素中添加/移除一个或多个类之间切换
unwrap() 移除被选元素的父元素
val() 设置或返回被选元素的属性值(针对表单元素)
width() 设置或返回被选元素的宽度
常用实例:

三级菜单展示

$(this).next().removeClass('hide').parent().siblings().find('.items').addClass('hide')
1
直接操作css属性

// 两个参数设置属性
$('#p1').css('font-size','24px')
// 一个参数获取属性
$('#p1').css('font-size')

// 一次设置多个属性
$('#p1').css({"border":"1px solid black","color":"blue"})
1
2
3
4
5
6
7
位置操作

// 不加参数获取位置参数
$(".c3").offset()
// 加参数设置位置参数
$(".c3").offset({top:284,left:400})

// position只能获取值,不能设置值

// scrollTop获取当前滚动条偏移量
$('window').scrollTop();
$('window').scrollTop(0);  // 设置滚动条偏移量
1
2
3
4
5
6
7
8
9
10
文本操作

text() html() 不加参数获取值,加参数设置值
val() 不加参数获取值,加参数设置值
1
2
属性操作

// 获取文本属性
$('#d1').attr('s1')  // 获取属性值
$('#d1').attr('s1','haha')  // 设置属性值
$('#d1').attr({'num':50,'taidi':'gay'})  // 设置多个属性
$('#d1').removeAttr('taidi')  // 删除一个属性

// 获取check与radio标签的checked属性
$('#i1').prop('checked')
$('#i1').prop('checked',true)
1
2
3
4
5
6
7
8
9
文档处理

// 标签内部尾部追加元素
$('#d1').append(pEle)
$pEle.appendTo($('#d1'))

// 标签内部头部添加元素
$('#d1').prepend(pEle)
$pEle.prependTo($('#d1'))

// 标签外部下面添加元素
$(A).after(B)// 把B放到A的后面
$(A).insertAfter(B)// 把A放到B的后面

// 标签外部上面添加元素
$(A).before(B)// 把B放到A的前面
$(A).insertBefore(B)// 把A放到B的前面

// 替换标签
replaceWith()  // 什么被什么替换
replaceAll()  // 拿什么替换什么

// 克隆事例
<button id="b2">屠龙宝刀,点击就送</button>
// clone方法加参数true,克隆标签并且克隆标签带的事件
  $("#b2").on("click", function () {
    $(this).clone(true).insertAfter(this);  // true参数
  });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
表单筛选器
选择器 实例 选取
:input $(":input") 所有 input 元素
:text $(":text") 所有带有 type=“text” 的 input 元素
:password $(":password") 所有带有 type=“password” 的 input 元素
:checkbox $(":checkbox") 所有带有 type=“checkbox” 的 input 元素
:submit $(":submit") 所有带有 type=“submit” 的 input 元素
:reset $(":reset") 所有带有 type=“reset” 的 input 元素
:button $(":button") 所有带有 type=“button” 的 input 元素
:image $(":image") 所有带有 type=“image” 的 input 元素
:file $(":file") 所有带有 type=“file” 的 input 元素
:enabled $(":enabled") 所有启用的 input 元素
:disabled $(":disabled") 所有禁用的 input 元素
:selected $(":selected") 所有选定的 input 元素
:checked $(":checked") 所有选中的 input 元素
// 针对表单内的标签
$('input[type="text"]')
// 简化写法
$(':text')

// 找到所有被选中的checkbox
$(':checkbox')  // 注意select框中默认selected标签也会被找到
$('input:checkbox')
1
2
3
4
5
6
7
8
遍历方法
方法 描述
add() 把元素添加到匹配元素的集合中
children() 返回被选元素的所有直接子元素
closest() 返回被选元素的第一个祖先元素
contents() 返回被选元素的所有直接子元素(包含文本和注释节点)
each() 为每个匹配元素执行函数
filter() 把匹配元素集合缩减为匹配选择器或匹配函数返回值的新元素
find() 返回被选元素的后代元素
first() 返回被选元素的第一个元素
is() 根据选择器/元素/jQuery 对象检查匹配元素集合,如果存在至少一个匹配元素,则返回 true
last() 返回被选元素的最后一个元素
next() 返回被选元素的后一个同级元素
nextAll() 返回被选元素之后的所有同级元素
nextUntil() 返回介于两个给定参数之间的每个元素之后的所有同级元素
not() 从匹配元素集合中移除元素
offsetParent() 返回第一个定位的父元素
parent() 返回被选元素的直接父元素
parents() 返回被选元素的所有祖先元素
parentsUntil() 返回介于两个给定参数之间的所有祖先元素
prev() 返回被选元素的前一个同级元素
prevAll() 返回被选元素之前的所有同级元素
prevUntil() 返回介于两个给定参数之间的每个元素之前的所有同级元素
siblings() 返回被选元素的所有同级元素 Returns all sibling elements of the selected element
slice() 把匹配元素集合缩减为指定范围的子集
each() 方法为每个匹配元素规定要运行的函数。
$.each(array,function(index){
  console.log(array[index])
})
$.each(array,function(){
  console.log(this);
})
// 支持简写
$divEles.each(function(){
  console.log(this)  // 标签对象
})
1
2
3
4
5
6
7
8
9
10
11
jQuery链式操作
python代码诠释链式调用,其实就是在调用方法之后讲对象再次返回

<div>
<p>p1</p>
<p>p2</p>
</div>
$('div>p').first().addclass('c1').next().addclass('c2');
1
2
3
4
5
jQuery 事件
事件处理程序指的是当 HTML 中发生某些事件时所调用的方法。

$(document).ready()
$(document).ready() 方法允许我们在文档完全加载完后执行函数。为了防止网页还没加载完,js代码就已经执行,通常利用下面两种方式来书写js代码。

$(document).ready(function(){
// 在这里写你的JS代码...
})

网页最后
$(function(){
// 在这里写你的代码
})
1
2
3
4
5
6
7
8
click()
click() 方法是当按钮点击事件被触发时会调用一个函数。

在下面的实例中,当点击事件在某个<p> 元素上触发时,隐藏当前的 <p> 元素:

$("p").click(function(){ 
  $(this).hide(); 
});
1
2
3
dblclick()
当双击元素时,会发生 dblclick 事件。

$("p").dblclick(function(){ 
  $(this).hide(); 
});
1
2
3
mouseenter()
当鼠标指针穿过元素时,会发生 mouseenter 事件。

$("#p1").mouseenter(function(){ 
  alert("鼠标经过了!"); 
});
1
2
3
mouseleave()
当鼠标指针离开元素时,会发生 mouseleave 事件。

$("#p1").mouseleave(function(){ 
  alert("鼠标离开了!"); 
});
1
2
3
mousedown()
当鼠标指针移动到元素上方,并按下鼠标按键时,会发生 mousedown 事件。

$("#p1").mousedown(function(){ 
  alert("鼠标按下!"); 
});
1
2
3
mouseup()
当在元素上松开鼠标按钮时,会发生 mouseup 事件。

ouseup(function(){ 
  alert("鼠标松开!"); 
});
1
2
3
hover()
hover()方法用于模拟光标悬停事件。当鼠标移动到元素上时,会触发指定的第一个函数(mouseenter);当鼠标移出这个元素时,会触发指定的第二个函数(mouseleave)。

$("#p1").hover(function(){ 
  alert("你的光标悬停!"); 
  }, 
  function(){ 
  alert("你的光标已经离开!"); 
});
1
2
3
4
5
6
focus()
当元素获得焦点时,发生 focus 事件。

$("input").focus(function(){ 
  $(this).css("background-color","#eee"); 
});
1
2
3
blur()
当元素失去焦点时,发生 blur 事件。

$("input").blur(function(){ 
  $(this).css("background-color","#eee"); 
});
1
2
3
input()
input实时监听,输入内容时发生 input 事件

 $('#i1').on('input',function () {
        console.log($(this).val())
    });
1
2
3
取消标签默认的事件
利用preventDefault()
$('input').click(function (e) {
        alert(123);
        e.preventDefault();  
    });
直接返回false
$('input').click(function (e) {
        alert(123);
        return false;
    });
  
1
2
3
4
5
6
7
8
9
10
11
事件冒泡
iv>p>span  // 三者均绑定点击事件
$("span").click(function (e) {
        alert("span");
        e.stopPropagation();  // 阻止事件冒泡
    });
1
2
3
4
5
事件委托
<button>按钮</button>
<script src="jQuery-3.3.1.js"></script>
<script>
    $('body').on('click','button',function () {
        alert(123)
    })
</script>
1
2
3
4
5
6
7
jQuery自带的动画效果
下面的表格列出了用于创建动画效果的 jQuery 方法。

方法 描述
delay() 对被选元素的所有排队函数(仍未运行)设置延迟
dequeue() 移除下一个排队函数,然后执行函数
fadeIn() 逐渐改变被选元素的不透明度,从隐藏到可见
fadeOut() 逐渐改变被选元素的不透明度,从可见到隐藏
fadeTo() 把被选元素逐渐改变至给定的不透明度
fadeToggle() 在 fadeIn() 和 fadeOut() 方法之间进行切换
finish() 对被选元素停止、移除并完成所有排队动画
hide() 隐藏被选元素
queue() 显示被选元素的排队函数
show() 显示被选元素
slideDown() 通过调整高度来滑动显示被选元素
slideToggle() slideUp() 和 slideDown() 方法之间的切换
slideUp() 通过调整高度来滑动隐藏被选元素
stop() 停止被选元素上当前正在运行的动画
toggle() hide() 和 show() 方法之间的切换
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

微信小程序--实现canvas绘图并且可以复盘回看

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

目录结构:



index.wxml:

<view class="canvasBox">
  <canvas canvas-id="myCanvas" class="myCanvas" catchtouchstart='canvasStart' catchtouchmove='canvasMoving'></canvas>
</view>
<view class="btn">
  <button type="warn" bindtap='drawPen'>画笔</button>
  <button type="primary" bindtap='clearCanvas'>清空画板</button>
  <button type="warn" bindtap='clearLine'>橡皮擦</button>
  <button style='background:#000;color:#fff;' bindtap="black">黑色</button>
  <button style='background:yellow;color:#000;' bindtap="yellow">黄色</button>
  <button style='background:red;color:#fff;' bindtap="red">红色</button>
  <button style='background:blue;color:#fff;' bindtap="blue">蓝色</button>
  <button style='background:green;color:#fff;' bindtap="green">绿色</button>
  <button type="warn" bindtap="startRecording">开始录制</button>
  <button type="primary" bindtap='rePlay'>复盘</button>
  <button></button>
</view>
index.wxss:

.canvasBox{
  position: relative;
  top:0;
  left:0;
  width: 750rpx;
  height:800rpx;
  background:#eee;
}
.canvasBox .myCanvas{
  width: 100%;
  height:100%;
  position: absolute;
  top:0;
  left:0;
}
 
.btn{
  width: 750rpx;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
}
.btn button{
  width: 180rpx;
  font-size: 24rpx;
}
index.js:

//index.js
//获取应用实例
import {hisData} from "../../utils/historyOperation.js";
const app = getApp()
var moveToX = 0, moveToY = 0, lineToX = 0, lineToY = 0;
var context = null;
var isStart = false;
var date;
var startDate;//开始时刻
var penType = "drawPen";
var colorStr = "#000";
var operationType = "mapping";
Page({
  data: {
    
  },
  
  canvasStart:function(e){
    var x = Math.floor(e.touches[0].clientX);
    var y = Math.floor(e.touches[0].clientY);
    date = new Date();
    moveToX = x;
    moveToY = y;
    operationType = "mapping";
    if(penType === "clearPen"){
      operationType = "clearLine";
    }
    if (isStart) {
      hisData.hisDataArr.push({
        time: date.getTime() - startDate,
        operation: operationType,
        lineArr: {
          startX: moveToX,
          startY: moveToY,
          currentX: x,
          currentY: y,
          z: 1,
          colorStr:colorStr
        }
      })
    }
  },
  //绘制线条
  canvasMoving:function(e){
    date = new Date();
    var x = Math.floor(e.changedTouches[0].clientX);
    var y = Math.floor(e.changedTouches[0].clientY);
    lineToX = x;
    lineToY = y;
    if(penType === "clearPen"){
      operationType = "clearLine";
      context.clearRect(x-12, y-12, 24, 24);
      context.draw(true);
    }else{
      operationType = "mapping";
      context.setStrokeStyle(colorStr);
      context.moveTo(moveToX, moveToY);
      context.lineTo(lineToX, lineToY);
    }
    if (isStart) {
      hisData.hisDataArr.push({
        time: date.getTime() - startDate,
        operation: operationType,
        lineArr: {
          startX: moveToX,
          startY: moveToY,
          currentX: lineToX,
          currentY: lineToY,
          z: 1,
          colorStr: colorStr
        }
      })
    }
    moveToX = lineToX;
    moveToY = lineToY;
    context.stroke();
    context.draw(true);
  },
  
  clearCanvas:function(){
    context.clearRect(0,0,375,400);
    context.draw(true);
    date = new Date();//记录当前操作时刻
    operationType = "clearCanvas";
    if(isStart){
      hisData.hisDataArr.push({
        time: date.getTime() - startDate,
        operation: operationType,
        lineArr: {
          startX: -1,
          startY: -1,
          currentX: -1,
          currentY: -1,
          z: 0,
          colorStr: colorStr
        }
      })
    }
  },
  
  drawPen:function(){
    penType = "drawPen";
  },
  clearLine:function(){
    penType = "clearPen";
  },
  black:function(){
    colorStr = "#000";
  },
  yellow: function () {
    colorStr = "yellow";
  },
  red: function () {
    colorStr = "red";
  },
  blue: function () {
    colorStr = "blue";
  },
  green: function () {
    colorStr = "green";
  },
  startRecording:function(){
    isStart = true;
    date = new Date();
    startDate = date.getTime();
  },
  rePlay:function(){
    wx.navigateTo({
      url: '../replay/replay',
    })
  },
  onLoad: function () {
    isStart = false;
    context = wx.createCanvasContext('myCanvas');
    context.beginPath();
    context.setStrokeStyle('#000');
    context.setLineWidth(5);
    context.setLineCap('round');
    context.setLineJoin('round');
  }
})
historyOperation.js:该文件用来保存历史操作,以便复盘

const hisData = {
  hisDataArr:[
    {
      time:0,//操作时间
      /**
       * 操作类型
       * 绘图:mapping
       * 拖动球员:moveplayer
       * 清除画布:clearCanvas
       * 橡皮擦:clearLine
       */
      operation:"mapping",//操作类型
      /**
       * 绘制路径
       * startX:开始x坐标
       * startY:开y纵坐标
       * currentX:目标位置的 x 坐标
       * currentY:目标位置的 y 坐标
       * z:1代表画线时鼠标处于move状态,0代表处于松开状态
       * colorStr:线的填充颜色
       */
      lineArr: {    //绘制路径
        startX:0,
        startY:0,
        currentX:0,
        currentY:0,
        z:0,
        colorStr:"#000"
      }
    }
  ]
};
 
export {hisData};
复盘:

reply.wxml:

<!--pages/replay/replay.wxml-->
<view class="replayBox">
  <canvas canvas-id='myCanvas' class="myCanvas"></canvas>
</view>
<button type="warn" bindtap="start">开始</button>
reply.wxss:

/* pages/replay/replay.wxss */
.replayBox{
  position:relative;
  width: 750rpx;
  height:800rpx;
  background: #eee;
}
 
.replayBox .myCanvas{
  position: absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
}
 
reply.js:

// pages/replay/replay.js
import {hisData} from "../../utils/historyOperation.js";
var startDate;
var date;
var curTime;
var context = null;
var timer = null;
Page({
 
  /**
   * 页面的初始数据
   */
  data: {
 
  },
  start:function(){
    context.clearRect(0, 0, 375, 400);
    clearInterval(timer);
    date = new Date();
    startDate = date.getTime();
    var i = 0;
    var that = this;
    var len = hisData.hisDataArr.length;
    timer = setInterval(function(){
      date = new Date();
      curTime = date.getTime() - startDate;
      if (curTime >= hisData.hisDataArr[i].time){
        switch (hisData.hisDataArr[i].operation) {
          case "mapping":
            context.setStrokeStyle(hisData.hisDataArr[i].lineArr.colorStr);
            context.moveTo(hisData.hisDataArr[i].lineArr.startX, hisData.hisDataArr[i].lineArr.startY);
            context.lineTo(hisData.hisDataArr[i].lineArr.currentX, hisData.hisDataArr[i].lineArr.currentY);
            context.stroke();
            context.draw(true);
            break;
          case "clearCanvas":
            context.clearRect(0, 0, 375, 400);
            context.draw(true);
            break;
          case "clearLine":
            context.clearRect(hisData.hisDataArr[i].lineArr.currentX-12, hisData.hisDataArr[i].lineArr.currentY-12, 24, 24);
            context.draw(true);
            break;
        }
        i++;
      }
      if(i >= len){
        clearInterval(timer);
      }
    },2);
  },
  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    context = wx.createCanvasContext('myCanvas');
    context.beginPath();
    context.setStrokeStyle('#000');
    context.setLineWidth(3);
    context.setLineCap('round');
    context.setLineJoin('round');
  }
})
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、网站建设 平面设计服务

后端人员如何快速上手css(flex布局

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

网页布局(layout)是 CSS 的一个重点应用。毕竟简单的样式不能总依赖前端人员。
布局的传统解决方案,基于盒状模型,依赖 display 属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。

2009年,W3C 提出了一种新的方案----Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。

Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。

采用 Flex 布局的元素,称为 Flex 容器(flex container),简称"容器"。它的所有子元素自动成为容器成员,称为 Flex 项目(flex item),简称"项目"。


容器默认存在两根轴:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴的开始位置(与边框的交叉点)叫做main start,结束位置叫做main end;交叉轴的开始位置叫做cross start,结束位置叫做cross end。

项目默认沿主轴排列。单个项目占据的主轴空间叫做main size,占据的交叉轴空间叫做cross size。

先说第一个属性flex-direction 属性决定主轴的方向(即项目的排列方向)。
.box {
flex-direction: row | row-reverse | column | column-reverse;
}
row(默认值):主轴为水平方向,起点在左端。
row-reverse:主轴为水平方向,起点在右端。
column:主轴为垂直方向,起点在上沿。
column-reverse:主轴为垂直方向,起点在下沿。
废话少说,立刻上代码,验证下。

row:

众所周知,h1和p都是块级元素,都会纵向排列,一旦采用 Flex 布局,就以x和y轴为基础排序,
column:

不但模糊了块级元素和行内元素的区别,而且左右的排序也很灵活。
row-reverse:

flex-wrap属性
默认情况下,项目都排在一条线(又称"轴线")上。flex-wrap属性定义,如果一条轴线排不下,就会换行。
.box{
flex-wrap: nowrap | wrap | wrap-reverse;
}
(1)nowrap(默认):不换行。
(2)wrap:换行,第一行在上方。
(3)wrap-reverse:换行,第一行在下方。
wrap:

虽然他换行了,但是每行的元素是不是靠的太近了
在这时候就能用到
justify-content属性
justify-content属性定义了项目在主轴上的对齐方式。
.box {
justify-content: flex-start | flex-end | center | space-between | space-around;
}
它可能取5个值,具体对齐方式与轴的方向有关。下面假设主轴为从左到右。

flex-start:(默认值):左对齐
flex-end:右对齐
center: 居中
space-between:两端对齐,项目之间的间隔都相等。
space-around:每个项目两侧的间隔相等。所以,项目之间的间隔比项目与边框的间隔大一倍。

space-between:

但是还是有点不对劲,原来没有垂直居中。
align-items属性就出来了
align-items属性定义项目在交叉轴上如何对齐。
.box {
align-items: flex-start | flex-end | center | baseline | stretch;
}
它可能取5个值。具体的对齐方式与交叉轴的方向有关,下面假设交叉轴从上到下。
flex-start:交叉轴的起点对齐。
flex-end:交叉轴的终点对齐。
center:交叉轴的中点对齐。
baseline: 项目的第一行文字的基线对齐。
stretch(默认值):如果项目未设置高度或设为auto,将占满整个容器的高度。

还有很多属性可以灵活的调整页面的排版,我只举例最简单的几种排版。
没有float,没有position,也没有行和块。只要知道x轴和y轴就能轻松布局。
如果想了解flex布局,可以看一下这位大佬的博客:
语法篇:http://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
实战篇:http://www.ruanyifeng.com/blog/2015/07/flex-examples.html


前端开发——NodeJs学习

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

NodeJs基础
nodejs的本质:不是一门新的编程语言,nodejs是javascript运行在服务端的运行环境,编程语言还是javascript

global模块-全局变量
Node.js 中的全局对象是 global, 类似于浏览器中的window

常用的global属性

console: 用于打印日志
setTimeout/clearTimeout: 设置清除延时器
setInterval/clearInterval: 设置清除定时器

__dirname: 当前文件的路径,不包括文件名
__filename: 获取当前文件的路径,包括文件名

//与模块化相关的,模块化的时候会用到
require
exports
module

除了global模块中的内容可以直接使用,其他模块都是需要加载的。
其他模块不是全局的,不能直接使用。因此需要导入才能使用。
fs模块
fileSystem-- 文件系统,提供了一系列操作文件的API接口,可以方便我读写文件

读取文件
语法:fs.readFile(path[, options], callback)

方式一:不传编码参数

//参数1: 文件的名字
//参数2: 读取文件的回调函数
  //参数1:错误对象,如果读取失败,err会包含错误信息,如果读取成功,err是null
  //参数2:读取成功后的数据(是一个Buffer对象)
fs.readFile("data.txt", function(err, data){
  console.log(err);
  console.log(data);
  // 可以通过data.toString() 把二进制数据转成文本,当然前提是读取的文件本来就是文本,如果是图片获取的二进制就无法转换成文本
});

方式二:传编码参数

//参数1: 文件的路径
//参数2: 编码,如果设置了,返回一个字符串,如果没有设置,会返回一个buffer对象
//参数3: 回调函数
fs.readFile("data.txt", "utf8",function(err, data){
  console.log(err);
  console.log(data);
});

关于Buffer对象

1. Buffer对象是Nodejs用于处理二进制数据的。
2. 其实任意的数据在计算机底层都是二进制数据,因为计算机只认识二进制。
3. 所以读取任意的文件,返回的结果都是二进制数据,即Buffer对象
4. Buffer对象可以调用toString()方法转换成字符串。

写文件
语法:fs.writeFile(file, data[, options], callback)

//参数1:写入的文件名(如果文件不存在,会自动创建)
//参数2:写入的文件内容(注意:写入的内容会覆盖以前的内容)
//参数3:写文件后的回调函数
fs.writeFile("2.txt", "hello world, 我是一个中国人", function(err){
  if(err) {
    return console.log("写入文件失败", err);
  }
  console.log("写入文件成功");
});
1
2
3
4
5
6
7
8
9
注意:

写文件的时候,会把原来的内容给覆盖掉
追加文件
语法:fs.appendFile(path, data[, options], callback)

//参数1:追加的文件名(如果文件不存在,会自动创建)
//参数2:追加的文件内容(注意:写入的内容会覆盖以前的内容)
//参数3:追加文件后的回调函数
fs.appendFile("2.txt", "我是追加的内容", function(err){
  if(err) {
    return console.log("追加文件内容失败");
  }
  console.log("追加文件内容成功");
})


思考:如果没有appendFile,通过readFile与writeFile应该怎么实现?

文件同步与异步的说明
fs中所有的文件操作,都提供了异步和同步两种方式

异步方式:不会阻塞代码的执行
同步方式:会阻塞代码的执行
//同步方式
console.log(111);
var result = fs.readFileSync("2.txt", "utf-8");
console.log(result);
console.log(222);

总结:同步操作使用虽然简单,但是会影响性能,因此尽量使用异步方法,尤其是在工作过程中。

stream
stream是Node.js提供的又一个仅在服务区端可用的模块,目的是支持“流”这种数据结构。

什么是流?流是一种抽象的数据结构。想象水流,当在水管中流动时,就可以从某个地方(例如自来水厂)源源不断地到达另一个地方(比如你家的洗手池)。我们也可以把数据看成是数据流,比如你敲键盘的时候,就可以把每个字符依次连起来,看成字符流。这个流是从键盘输入到应用程序,实际上它还对应着一个名字:标准输入流(stdin)。

如果应用程序把字符一个一个输出到显示器上,这也可以看成是一个流,这个流也有名字:标准输出流(stdout)。流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位。

有些流用来读取数据,比如从文件读取数据时,可以打开一个文件流,然后从文件流中不断地读取数据。有些流用来写入数据,比如向文件写入数据时,只需要把数据不断地往文件流中写进去就可以了。

在Node.js中,流也是一个对象,我们只需要响应流的事件就可以了:data事件表示流的数据已经可以读取了,end事件表示这个流已经到末尾了,没有数据可以读取了,error事件表示出错了。

下面是一个从文件流读取文本内容的示例:

'use strict';

var fs = require('fs');

// 打开一个流:
var rs = fs.createReadStream('sample.txt', 'utf-8');

rs.on('data', function (chunk) {
    console.log('DATA:')
    console.log(chunk);
});

rs.on('end', function () {
    console.log('END');
});

rs.on('error', function (err) {
    console.log('ERROR: ' + err);
});

要注意,data事件可能会有多次,每次传递的chunk是流的一部分数据。

要以流的形式写入文件,只需要不断调用write()方法,最后以end()结束:

'use strict';

var fs = require('fs');

var ws1 = fs.createWriteStream('output1.txt', 'utf-8');
ws1.write('使用Stream写入文本数据...\n');
ws1.write('END.');
ws1.end();

var ws2 = fs.createWriteStream('output2.txt');
ws2.write(new Buffer('使用Stream写入二进制数据...\n', 'utf-8'));
ws2.write(new Buffer('END.', 'utf-8'));
ws2.end();


所有可以读取数据的流都继承自stream.Readable,所有可以写入的流都继承自stream.Writable。

pipe
就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来。一个Readable流和一个Writable流串起来后,所有的数据自动从Readable流进入Writable流,这种操作叫pipe。

在Node.js中,Readable流有一个pipe()方法,就是用来干这件事的。

让我们用pipe()把一个文件流和另一个文件流串起来,这样源文件的所有数据就自动写入到目标文件里了,所以,这实际上是一个复制文件的程序:

'use strict';

var fs = require('fs');

var rs = fs.createReadStream('sample.txt');
var ws = fs.createWriteStream('copied.txt');

rs.pipe(ws);

默认情况下,当Readable流的数据读取完毕,end事件触发后,将自动关闭Writable流。如果我们不希望自动关闭Writable流,需要传入参数:

readable.pipe(writable, { end: false });
1
path模块
路径操作的问题
具体的说明可以参考 NodeJs学习.md

在读写文件的时候,文件路径可以写相对路径或者绝对路径

//data.txt是相对路径,读取当前目录下的data.txt, 相对路径相对的是指向node命令的路径
//如果node命令不是在当前目录下执行就会报错, 在当前执行node命令的目录下查找data.txt,找不到
fs.readFile("data.txt", "utf8", function(err, data) {
  if(err) {
    console.log("读取文件失败", err);
  }

  console.log(data);
});

相对路径:相对于执行node命令的路径

绝对路径:__dirname: 当前文件的目录,__filename: 当前文件的目录,包含文件名

path模块的常用方法
关于路径,在linux系统中,路径分隔符使用的是/,但是在windows系统中,路径使用的\

在我们拼写路径的时候会带来很多的麻烦,经常会出现windows下写的代码,在linux操作系统下执行不了,path模块就是为了解决这个问题而存在的。

常用方法:

path.join();//拼接路径

//windows系统下
> path.join("abc","def","gg", "index.html")
"abc\def\gg\a.html"

//linux系统下
> path.join("abc","def","gg", "index.html")
'abc/def/gg/index.html'

http模块
创建服务器步骤

// 移入http模块
const http = require('http')
// 调用创建http 服务器的方法
const server = http.createServe()
// 给服务器注册request事件监听,每次浏览器像服务器发送请求的时候都会被监听到
server.on('request', function(request, response){
    // request 浏览器请求的数据,包括请求方式method 请求的地址 url等
    // response 浏览器的响应,可以设置响应头、响应体、响应状态码
    const method = request.method
    const url = request.url
    
    // 设置响应的状态码
    response.StatusCode = 404
    // 设置响应的头
    response.setHeader('Content-Type', 'text/html');
    // 设置响应体内容,write可以调用多次
    response.write('hello world!')
    // 响应结束
    response.end()
    
    // 如果在end(content),这样的写法相当于是让write和end的合写
    response.end('hello world!')
})
// 给服务器设置监听,相当于启动服务器
server.listen(8888,function(){
    console.log('服务器启动成功')
})

// 简写方式

http.createServer((req,res) => {
    ....
}).listen(8888,() => {
    ....
})

详细说明

给服务器注册request事件,只要服务器接收到了客户端的请求,就会触发request事件
request事件有两个参数,request表示请求对象,可以获取所有与请求相关的信息,response是响应对象,可以获取所有与响应相关的信息。
服务器监听的端口范围为:1-65535之间,推荐使用3000以上的端口,因为3000以下的端口一般留给系统使用
response对象详解
常见的属性和方法:

res.write(data): 给浏览器发送请求体,可以调用多次,从而提供连续的请求体
res.end();   通知服务器,所有响应头和响应主体都已被发送,即服务器将其视为已完成。
res.end(data); 结束请求,并且响应一段内容,相当于res.write(data) + res.end()
res.statusCode: 响应的的状态码 200 404 500
res.statusMessage: 响应的状态信息, OK Not Found ,会根据statusCode自动设置。
res.setHeader(name, value); 设置响应头信息, 比如content-type
res.writeHead(statusCode, statusMessage, options); 设置响应头,同时可以设置状态码和状态信息。
1
2
3
4
5
6
7
注意:必须先设置响应头,才能设置响应。

实现静态WEB服务器
服务器响应首页
注意:浏览器中输入的URL地址,仅仅是一个标识,不与服务器中的目录一致。也就是说:返回什么内容是由服务端的逻辑决定
server.on('request', function(req, res) {
  var url = req.url
  if(url === '/') {
    fs.readFile('./index.html', function(err, data) {
      if(err) {
        return res.end('您访问的资源不存在~')
      }

      res.end(data)
    })
  }
})

根据根据不同url,响应不同文件
content-type设置-MIME类型
MIME(Multipurpose Internet Mail Extensions)多用途Internet邮件扩展类型 是一种表示文档性质和格式的标准化方式
浏览器通常使用MIME类型(而不是文件扩展名)来确定如何处理文档;因此服务器将正确的MIME类型附加到响应对象的头部是非常重要的
MIME类型的通用处理-mime模块
作用:获取文件的MIME类型
安装:npm i mime
var mime = require('mime')

// 获取路径对应的MIME类型
mime.getType('txt')                    // ⇨ 'text/plain'
// 根据MIME获取到文件后缀名
mime.getExtension('text/plain')        // ⇨ 'txt'
1
2
3
4
5
6
有了这个模块我们就可以把设置响应头的代码改写成下面

// mime 不仅可以只写一个后缀名,还可以通过url来解析出后缀名来,因此这里可以直接写url
response.setHeader('content-type',mime.getType(request.url))
1
2
npm - Node包管理工具
初始化包
npm init;    //这个命令用于初始化一个包,创建一个package.json文件,我们的项目都应该先执行npm init
npm init -y;  //快速的初始化一个包, 不能是一个中文名
1
2
安装包
npm install 包名;  //安装指定的包名的版本到项目中
npm install 包名@版本号;  //安装指定包的指定版本

npm i 包名; //简写

卸载包
npm uninstall 包名;  //卸载已经安装的包
1
清除缓存
npm cache clean -f // 如果npm安装失败了,可以用这个命令来清除缓存
1
package.json文件
package.json文件,包(项目)描述文件,用来管理组织一个包(项目),它是一个纯JSON格式的。

作用:描述当前项目(包)的信息,描述当前包(项目)的依赖项
如何生成:npm init或者npm init -y
作用
作为一个标准的包,必须要有package.json文件进行描述
一个项目的node_modules目录通常都会很大,不用拷贝node_modules目录,可以通过package.json文件配合npm install直接安装项目所有的依赖项
描述内容
{
  "name": "03-npm",  //描述了包的名字,不能有中文
  "version": "1.0.0",  //描述了包的的版本信息, x.y.z  如果只是修复bug,需要更新Z位。如果是新增了功能,但是向下兼容,需要更新Y位。如果有大变动,向下不兼容,需要更新X位。
  "description": "", //包的描述信息
  "main": "index.js", //入口文件(模块化加载规则的时候详细的讲)
  "scripts": {  //配置一些脚本,在vue的时候会用到,现在体会不到
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],  //关键字(方便搜索)
  "author": "",  //作者的信息
  "license": "ISC",  //许可证,开源协议
  "dependencies": {   //重要,项目的依赖, 方便代码的共享  通过 npm install可以直接安装所有的依赖项
    "bootstrap": "^3.3.7",
    "jquery": "^3.3.1"
  }
}

注意:一个合法的package.json,必须要有name和version两个属性

本地安装和全局安装
有两种方式用来安装 npm 包:本地安装和全局安装。选用哪种方式来安装,取决于你如何使用这个包。

全局安装:如果你想将其作为一个命令行工具,那么你应该将其安装到全局。这种安装方式后可以让你在任何目录下使用这个命令。比如less命令,webpack命令,hcc-md命令 。
本地安装:如果你自己的模块依赖于某个包,并通过 Node.js 的 require 加载,那么你应该选择本地安装,这种方式也是 npm install 命令的默认行为。
// 全局安装,会把npm包安装到C:\Users\HUCC\AppData\Roaming\npm目录下,作为命令行工具使用
npm install -g 包名;

//本地安装,会把npm包安装到当前项目的node_modules文件中,作为项目的依赖
npm install 包名;  

常见的命令行工具
nrm
nrm:npm registry manager(npm仓库地址管理工具)
安装:npm i -g nrm
# 带*表示当前正在使用的地址

# 查看仓库地址列表
nrm ls

# 切换仓库地址
nrm use taobao

nodemon 自动重启
作用:监视到js文件修改后,自动重启node程序
安装:npm i -g nodemon
使用:nodemon app.js 运行node程序
模块化(module)
基本概念
在nodejs中,应用由模块组成,nodejs中采用commonJS模块规范。

一个js文件就是一个模块
每个模块都是一个独立的作用域,在这个而文件中定义的变量、函数、对象都是私有的,对其他文件不可见。
node中模块分类
1 核心模块
由 node 本身提供,不需要单独安装(npm),可直接引入使用
2 第三方模块
由社区或个人提供,需要通过npm安装后使用
3 自定义模块
由我们自己创建,比如:tool.js 、 user.js
核心模块
fs:文件操作模块
http:网络操作模块
path:路径操作模块
url: 解析地址的模块
querystring: 解析参数字符串的模块
基本使用:1 先引入 2 再使用
// 引入模块
var fs = require('fs');
1
2
第三方模块
第三方模块是由 社区或个人 提供的
比如:mime模块/art-template/jquery…
基本使用:1 先通过npm下载 2 再引入 3 最后使用
用户自定义模块
由开发人员创建的模块(JS文件)
基本使用:1 创建模块 2 引入模块
注意:自定义模块的路径必须以./获取../开头
// 加载模块
require('./a')     // 推荐使用,省略.js后缀!

require('./a.js')
1
2
3
4
模块导入
/* 
  nodejs中模块分为3大类
    1. nodejs本身提供的核心模块   fs http path url querystring
      核心模块不需要安装,直接导入即可。
      核心模块的加载语法: const fs = require('fs')
    
    2. 第三方模块  mime art-template
      第三方模块: 必须先安装(npm install XXX)  才能导入
      第三方模块的加载语法: npm install XXX   const mime = require('mime')
    
    3. 自定义的模块 一个js文件 
      不需要安装  只需要自己创建一个js文件
      自定义模块的加载语法:  require('模块的路径')  模块不能是名字,必须是路径  ./ ../ .js后缀是可以省略

  require加载规则(以mime模块为例)
  1. 判断是否是路径, 如果是  就是自定义模块
  2. 如果是名字 判断是否是核心模块
  3. 如果是第三方模块  在当前目录找node_modules
  4. 在node_modules中查找mime文件夹
  5. 查找是否有package.json, 查看是否main属性
  6. 判断是否有main, 如果没有,默认查找index.js  index.json index.node
  7. 如果没有
  8. 如果找不到,就去上一层目录,一直找到根目录
  9, 如果还没有,就说明模块不存在
*/

模块导出
/* 
  1. 模块中定义的变量和函数都是私有的
  2. 任意的一个模块中, 都有自带一个属性 module (全局属性) module代表的就是当前的这个模块。
  3. module中有一个属性  exports ,这个exports属性是一个对象,代表的就是当前模块的导出 module.exports当前模块唯一能够被外界访问到的

*/
//通过module.exports对外导出一些值
module.exports = 值  只能导出一个值
module.exports = {}  可以把所有要导出的内容都放到一个新的对象中
module.export.xxx = 值
/* 
  在任意的模块中 module.exports表示该模块的导出
  为了我们方便导出, 每个模块中还提供了 exports  
  exports 初始状态下,和module.exports指向了同一个对象。

  注意点: 如果通过exports的方式来导出内容,只能给对象增加属性 不能替换这个对象
*/
// 我们真正到处的对象是module.exports指向的对象
exports = {} // 这样只是改了exports的指向,而module.exports的指向没有改变,所以这样是不对的
// 以下这种是允许的
exports.xxx = '值'

express与mysql
首先需要安装mysql模块

npm i mysql
1
基本使用
// 导入第三方包
const mysql = require('mysql')
// 创建连接
var connection = mysql.createConnection({
  // 本地
  host: 'localhost',
  user: 'root',
  password: 'root',
  // 数据库名称
  database: 'mydb',
  port: 3306
})

// 连接数据库
connection.connect()

// 执行sql语句
connection.query('select * from user where id = 8', (err, result) => {
  if (err) return console.log('查询失败', err)
  // result返回的是数组, 数组中是一个对象
  console.log(result)
})

// 关闭连接
connection.end()


查询语句
var name = 'zs'
// 使用?表示占位,可以防止sql注入
connect.query(`select * from user where name=?`, name, (err, result) => {
  if (err) return console.log('错误了', err)
  console.log(result)
})
1
2
3
4
5
6
插入语句
connect.query(
  'insert into user (name, age, gender, content) values (?, ?, ?, ?)',
  ['zs', 18, '男', '哈哈哈哈'],
  err => {
    if (err) return console.log('错误', err)
    console.log('添加成功了')
  }
)

// 方式2
connect.query(
  'insert into user set ?',
  {
    name: 'zs',
    age: 30,
    gender: '男',
    content: '哈哈哈'
  },
  (err, result) => {
    if (err) return console.log('错误', err)
    console.log('添加成功了', result)
  }
)


修改语句
connect.query(
  'update user set ? where id = ?',
  [
    {
      name: 'zs',
      age: 30,
      gender: '男',
      content: '哈哈哈'
    },
    10
  ],
  (err, result) => {
    if (err) return console.log('错误', err)
    console.log('添加成功了', result)
  }
)

删除语句
connect.query('delete from user where id = ?', 10, (err, result) => {
  if (err) return console.log('失败', err)
  console.log(result)
})
1
2
3
4
登录状态保持
http是无状态的,但是随着技术的发展,我们需要记住某些东西,但是因为http是无状态的,无法让服务器记住东西,因此就引入了cookie和session这两个东西,cookie用于浏览器端,session用于服务器端。

以用户登录为例:

当用户登录时,浏览器会给服务器发送请求,这时候服务器就会开辟一个空间用于存放session数据,并且会把生成的sessionId返回给浏览器,存放在浏览器的cookie中,之后浏览器在请求服务器的时候,就会去比对是否存在这个session。这样你的登录状态就已经保持下来了

cookie的特点

cookie大小只有4k
cookie每次请求的时候,都会自动携带
cookie可以设置过期时间
为了方便使用,我们可以使用express-session这个包,可以很方便使用session

express-session步骤:
1. 下载  npm i express-session
2. 导入  const session = require("express-session")
3. 使用session中间件
app.use(session({
    secret: 'itcast',
    // 设置浏览器端cookie的名字, 默认connect.sid
    name: 'itcast',
    resave: false,
    // 在浏览器和服务器连接的第一时间,分配session  给浏览器指定一个cookie
    saveUninitialized: true
}))
可以通过req.session访问到session
4. 登录成功,把登录成功的用户信息存储到 req.session.xxx中
5. 提供一个中间件,这个中间件在路由的前面,判断 req.session.xxx是否有值,有值,放走,没值,去登录,细节: 如果是/login 直接放走
6. 退出功能:  把req.session.xxx 清空即可


浏览器登录和退出
1. 登录做什么  把用户名和密码给服务器
2. 退出做什么, 1. 告诉服务器,要退出   2.(清缓存也行)

yarn和npm的说明
官网:https://yarn.bootcss.com/

Yarn是由Facebook、Google、Exponent 和 Tilde 联合推出了一个新的 JS 包管理工具 ,Yarn 是为了弥补 npm 的一些缺陷而出现的。

Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。
同时利用并行下载以最大化资源利用率,因此安装速度更快。
yarn的用法和npm的用法差不多
yarn命令
初始化一个新项目
yarn init
1
添加依赖包
yarn add 包名
1
升级依赖包
yarn upgrade 包名
1
移除依赖包
yarn remove 包名
1
安装项目的全部依赖
yarn
1
全局安装
yarn global add 包名
1
使用gulp自动化构建
官网:https://gulpjs.com/

中文文档:https://www.gulpjs.com.cn/

用自动化构建工具增强你的工作流程!

在开发过程中,有很多重复性的工作需要执行。

less转成css
对css代码压缩混淆
对js代码压缩混淆
写完代码后需要刷新浏览器
无法共用模版
gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器;她不仅能对网站资源进行优化,而且在开发过程中很多重复的任务能够使用正确的工具自动完成;使用她,我们不仅可以很愉快的编写代码,而且大大提高我们的工作效率。

gulp -----> grunt ------>webpack

环境安装
初始化项目
npm init -y
1
全局安装gulp
npm install gulp -g 
yarn global add gulp
1
2
作为项目的依赖进行安装
yarn add gulp --save-dev      或者    
yarn add gulp --save-dev
--save-dev 等同于 -D
如果这个依赖包只是在开发阶段需要用到,需要加-D
1
2
3
4
新建gulpfile.js文件
// 参数1: 任务名
// 参数2: 任务需要执行的内容
gulp.task('aa', function() {
  console.log('哈哈')
})
1
2
3
4
5
执行任务
gulp 任务名;

gulp; 如果不接任务名,那么会执行默认的 default任务
1
2
3
glup任务-文件拷贝-lib
文件拷贝使用到了gulp提供的几个核心方法
gulp.task: 定义任务

gulp.src() 读取文件

gulp.pipe() 把文件交给管道处理

gulp.dest() 输出文件到某个目录

gulp.task定义任务
gulp.src('./src/lib/**/*.*')把文件读取成一个文件流
gulp.pipe() 把文件流交给下一个流
gulp.dest('./dist/lib')输出文件
// 简单拷贝, 处理 lib文件夹, lib文件不需要做任何的处理,只需要拷贝到dist目录
// 任务需要加一个return, 表示任务完成
gulp.task('lib', function() {
  // 读取文件
  // gulp.src() 读取文件
  // gulp.pipe() 管道
  // gulp.dest() 放到哪儿
  return gulp.src('./src/lib/**/*.*').pipe(gulp.dest('./dist/lib'))
})
1
2
3
4
5
6
7
8
9
gulp任务-js代码压缩与混淆
gulp-uglify-es: 给js代码进行压缩,处理ES6的代码

gulp-rename: 重命名

安装依赖
yarn add gulp-uglify-es --save-dev 
1
配置任务
const uglify = require('gulp-uglify-es').default

gulp.task('js', function() {
  return gulp
    .src('./js/*.js')
    .pipe(uglify())
    .pipe(gulp.dest('./dist/js'))
})
1
2
3
4
5
6
7
8
安装重命名依赖
yarn add gulp-rename -D
1
重命名配置
task('js', function() {
  return src('./js/*.js')
    .pipe(dest('./dist/js'))
    .pipe(uglify())
    .pipe(
      rename({
        // 配置重命名的后缀名
        suffix: '.min'
      })
    )
    .pipe(dest('./dist/js'))
})
1
2
3
4
5
6
7
8
9
10
11
12
gulp任务-less处理
gulp-less: 把less变成css

gulp-rename: 重命名

gulp-minify-css: 压缩css代码

安装依赖包
yarn add gulp-less -D
1
less任务
// less任务
task('less', function() {
  return src('./less/*.less')
    .pipe(less())
    .pipe(
      rename({
        extname: '.css'
      })
    )
    .pipe(dest('./dist/css'))
})
1
2
3
4
5
6
7
8
9
10
11
安装css压缩处理包
yarn add gulp-minify-css -D
1
压缩css
// less任务
task('less', function() {
  return src('./less/*.less')
    .pipe(less())
    .pipe(
      rename({
        extname: '.css'
      })
    )
    .pipe(dest('./dist/css'))
    .pipe(minifycss())
    .pipe(
      rename({
        suffix: '.min'
      })
    )
    .pipe(dest('./dist/css'))
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gulp任务-图片压缩
gulp-imagemin: 可以对图片进行压缩

gulp-cache: 图片压缩比较耗时的一个任务, 如果每次都对所有的图片进行重新压缩,比较浪费时间, 会缓存下来所有已经压缩过的图片

安装依赖
yarn add gulp-imagemin -D
1
压缩图片的任务
task('image', function() {
  return src('./img/*')
    .pipe(imagemin())
    .pipe(dest('./dist/img'))
})
1
2
3
4
5
安装gulp-cachae
yarn add  gulp-cache -D
1
压缩图片是比较耗时的,我们可以使用gulp-cache来缓存已经压缩过的图片
task('image', function() {
  return src('./img/*')
    .pipe(cache(imagemin()))
    .pipe(dest('./dist/img'))
})
1
2
3
4
5
参考资料:https://www.cnblogs.com/yuzhongwusan/p/5417090.html

gulp任务-处理html
gulp-minify-html: 压缩html文件

gulp-html-extend: 语句当前html去导入另一个html

压缩html
yarn add gulp-minify-html -D
1
使用
// 处理html
task('html', function() {
  return src('./src/*.html')
    .pipe(minifyHtml())
    .pipe(dest('./dist'))
})
1
2
3
4
5
6
导入html
yarn add gulp-html-extend -D
1
// 处理html
task('html', function() {
  return src('./src/*.html')
    .pipe(extender())
    .pipe(minifyHtml())
    .pipe(dest('./dist'))
})
1
2
3
4
5
6
7
在页面中,如何导入html

<!-- @@include ./template/header.html -->
1
gulp任务-清空任务
安装
yarn add del -D
1
配置任务
task('clean', function() {
  return del('./dist')
})
1
2
3
gulp-任务整合series
task('build', gulp.series('clean', 'html', 'less', 'js', 'image', 'lib'))
1
gulp任务-监听文件的变化
// 实现一个,修改代码,会自动执行任务
// 监听的任务,,,,,,做一件事件,当我们修改了对应的文件,需要执行对应的任务
// gulp.watch() 监视文件
task('watch', function() {
  // 参数1:监视的文件
  // 参数2: 对应的任务, 多个任务
  watch('./src/**/*.html', series('html'))
  watch('./src/less/*.less', series('less'))
  watch('./src/js/*.js', series('js'))
  watch('./src/lib/**/*.*', series('lib'))
  watch('./src/img/*.*', series('img'))
})

gulp任务-自动刷新
安装
yarn add gulp-connect -D
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

vue路由加载页面时,数据返回慢的问题

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

场景:

vue路由加载页面时,数据返回慢的时候页面会有闪动的效果,数据加载前和加载后的区别。(特别是el-table表格数据)

思路:

路由前加载数据,等数据加载完再路由渲染页面

解决方案:

使用vue-router的 路由守卫 beforeRouteEnter,组件内直接定义以下路由导航守卫,和钩子函数的写法一样,下面列出三种路由守卫:

 beforeRouteEnter(to,from,next)0{
        // 在渲染该组件的对应路由被 confirm 前调用// 不!能!获取组件实例 `this`
        // 因为当守卫执行前,组件实例还没被创建
    } 复制代码
 beforeRouteUpdate(to,from,next){
        // 在当前路由改变,但是该组件被复用时调用
        // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
        // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
        // 可以访问组件实例 `this`
    } 复制代码
 beforeRouteLeave(to,from,next){
        // 导航离开该组件的对应路由时调用// 可以访问组件实例 `this`
    } 复制代码

vue-router详细,具体访问:导航守卫 数据获取

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务


layui数据表格如何加工具栏?

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

实现的最终效果图(文末会贴出全部代码,方便参考) 



1, 将此行代码加在红线所在的位置

,{fixed: 'right', width: 150, align:'center', toolbar: '#toolbarDemo'}


2,在合适的HTML位置加入 

<script type="text/html" id="toolbarDemo">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm" lay-event="update">编辑</button>
        <button class="layui-btn layui-btn-danger layui-btn-sm" lay-event="delete">删除</button>
    </div>
</script>
全部代码:

{include file="common/header"}

<div class="x-nav">
      <span class="layui-breadcrumb">
        <a href="">首页</a>
        <a href="">演示</a>
        <a>
          <cite>导航元素</cite></a>
      </span>
    <a class="layui-btn layui-btn-small" style="line-height:1.6em;margin-top:3px;float:right" href="javascript:location.replace(location.href);" title="刷新">
        <i class="layui-icon" style="line-height:30px">ဂ</i></a>
</div>

<table class="layui-hide" id="test" lay-size="sm" lay-filter="test"></table>

<script type="text/html" id="toolbarDemo">
    <div class="layui-btn-container">
        <button class="layui-btn layui-btn-sm" lay-event="update">编辑</button>
        <button class="layui-btn layui-btn-danger layui-btn-sm" lay-event="delete">删除</button>
    </div>
</script>

<script>
    layui.use('table', function(){
        var table = layui.table;

        table.render({
            elem: '#test'
            ,url:"{:url('admin/user/page')}"
            ,page: { //支持传入 laypage 组件的所有参数(某些参数除外,如:jump/elem) - 详见文档
                layout: ['limit', 'count', 'prev', 'page', 'next', 'skip'] //自定义分页布局
                //,curr: 5 //设定初始在第 5 页
                ,limit:10 //一页显示多少条
                ,limits:[5,10,15]//每页条数的选择项
                ,groups: 2 //只显示 2 个连续页码
                ,first: "首页" //不显示首页
                ,last: "尾页" //不显示尾页
            }
            ,cols: [[
                {field:'id', width:80, title: '代理ID', sort: true}
                ,{field:'username', width:100, title: '代理名称'}
                ,{field:'level', width:100, title: '代理级别'}
                ,{field:'email', width:80, title: '电子邮箱'}
                ,{field:'phone', width:280, title: '手机号'}
                ,{field:'agent_id', width:280, title: '上级代理ID'}
                ,{field:'status', width:280, title: '是否启用'}
                ,{field:'isAuth', width:280, title: '是否已经认证'}
                ,{field:'money', width:280, title: '金额'}
                ,{field:'created_time', width:280, title: '访问时间',sort: true}
                ,{fixed: 'right', width: 150, align:'center', toolbar: '#toolbarDemo'}
            ]]

        });


        //监听工具条
        table.on('tool(test)', function(obj){ //注:tool是工具条事件名,test是table原始容器的属性 lay-filter="对应的值"
            console.log(obj);
            var data = obj.data; //获得当前行数据
            var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值)
            var tr = obj.tr; //获得当前行 tr 的DOM对象

            if(layEvent === 'detail'){ //查看
                //do somehing
            } else if(layEvent === 'del'){ //删除
                layer.confirm('真的删除行么', function(index){
                    obj.del(); //删除对应行(tr)的DOM结构,并更新缓存
                    layer.close(index);
                    //向服务端发送删除指令
                });
            } else if(layEvent === 'edit'){ //编辑
                //do something

                //同步更新缓存对应的值
                obj.update({
                    username: '123'
                    ,title: 'xxx'
                });
            }
        });

    });

</script>
</body>
</html>

蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

Echarts 动态更新散点图

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

 最近遇到一个作业,要求使用 Echarts 散点图,本来这个图是很容易的,官网上也有很多的教程。但是如果可以动态的更新 Echarts 散点图就更好了。我本身对 js 不感兴趣,经过不停的查找资料最终实现了这一功能。
我的项目是 Servlet + jsp + Echarts。先从 Servlet 入手,我们的项目需要传递的数值是 x 坐标和 y 坐标。我首先写了一个 JavaBean

julie.java

package JavaBean;

public class julei {
    public julei(double x, double y) {
        this.x = x;
        this.y = y;
    }
    double x;

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    double y;

    @Override
    public String toString() {
        return "[" + this.x + "," + this.y + "]";
    }
}


Servlet中的代码,因为使用的是 json 来传递的数据,所以 json 相关的包还是少不了的。
BackServlet

package Servlet;

import JavaBean.Readtxt;
import JavaBean.julei;
import org.json.JSONArray;
import org.json.JSONObject;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@javax.servlet.annotation.WebServlet("/BackServlet")
public class BackServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {



        List<julei> list = new ArrayList<>();
        try {
            list = Readtxt.out();//这是我们项目中的一个类,不重要。
        }catch (Exception e)
        {
            System.out.println(e.toString());
        }


        JSONArray jsonArray = new JSONArray(list);
        System.out.println(jsonArray.toString());
        //最重要的就是这一句,将数据发送给谁来申请的位置
        response.getWriter().write(jsonArray.toString());


    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

    }
}


好了,现在到了最重要的前端方面了。
index.jsp
正常声明散点图,只要把data声明为空就好。

  var myChart = echarts.init(document.getElementById('man'));
          var option = {
            title : {
              text: '死亡分布图',
            },
            xAxis: {axisTick: {//决定是否显示坐标刻度
                alignWithLabel: true,
                show:true
              },},
            yAxis: {},
            series: [{
              symbolSize: 20,
              data: [],
              type: 'scatter'
            }]
          };
          myChart.setOption(option);

数据接收部分:

var num = [];
          var gao = new Array();
          $.ajax({
            type : "post",
            async : true, //异步请求(同步请求将会锁住浏览器,其他操作须等请求完成才可执行)
            url : "BackServlet", //请求发送到TestServlet
            data : {},
            dataType : "json", //返回数据形式为json

            //7.请求成功后接收数据name+num两组数据
            success : function(result) {
              //result为服务器返回的json对象
              if (result) {
                //8.取出数据存入数组

                for (var i = 0; i < result.length; i++) {

                  gao.push([result[i].x,result[i].y]);//这一句很重要,它将数据转化为了正确的格式。

                }

                myChart.hideLoading(); //隐藏加载动画

                //9.覆盖操作-根据数据加载数据图表
                myChart.setOption({
                  series : [ {
                    // 根据名字对应到相应的数据
                    data : gao//在这里对data进行赋值。
                  } ]
                });

              }

            },
            error : function(errorMsg) {
              //请求失败时执行该函数
              alert("图表请求数据失败!");
              myChart.hideLoading();
            }
          })




完毕

我把整个jsp都放上来了,但是里面的 css 还有 js 就不放了,重点是传数据的那一部分。

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>LOL数据分析</title>
  <script src="https://cdn.bootcss.com/jquery/1.10.2/jquery.min.js"></script>
  <script type="text/javascript" src='js/echarts.js'></script>
  <link rel="stylesheet" href="css/jquery.fullPage.css">
  <link rel="stylesheet" href="css/style.css">
  <link rel="icon" href="https://jscdn.com.cn/highcharts/images/favicon.ico">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://code.highcharts.com.cn/highcharts/highcharts.js"></script>
  <script src="https://code.highcharts.com.cn/highcharts/modules/exporting.js"></script>
  <script src="https://img.hcharts.cn/highcharts-plugins/highcharts-zh_CN.js"></script>
</head>

<body>
<div class="bgcolor">
  <div style="z-index:100;" id="dowebok">
    <!--第一屏-->

    <div class="section">
      <div class="ly-box01">
        <img class="ly-img01" src="img/logol.png" style="width:100%;height:100%">

      </div>
    </div>

 

    <!--第三屏-->

    <div class="section">
      <div class="timeline"></div>
      <div class="timepoint21"></div>
      <div class="ly-box11">
        <div id="man" style=" width: 600px;height: 500px;"></div>

        <script type="text/javascript">
          // 基于准备好的dom,初始化echarts实例
          var value=[];
          $.ajaxSettings.async=false;
          var myChart = echarts.init(document.getElementById('man'));
          var option = {
            title : {
              text: '死亡分布图',
            },
            xAxis: {axisTick: {//决定是否显示坐标刻度
                alignWithLabel: true,
                show:true
              },},
            yAxis: {},
            series: [{
              symbolSize: 20,
              data: [],
              type: 'scatter'
            }]
          };
          myChart.setOption(option);






          var num = [];
          var gao = new Array(4);
          $.ajax({
            type : "post",
            async : true, //异步请求(同步请求将会锁住浏览器,其他操作须等请求完成才可执行)
            url : "BackServlet", //请求发送到TestServlet
            data : {},
            dataType : "json", //返回数据形式为json

            //7.请求成功后接收数据name+num两组数据
            success : function(result) {
              //result为服务器返回的json对象
              if (result) {
                //8.取出数据存入数组

                for (var i = 0; i < result.length; i++) {
                  gao.push([result[i].x,result[i].y]);
                }

            //  document.write(gao);
                myChart.hideLoading(); //隐藏加载动画

                //9.覆盖操作-根据数据加载数据图表
                myChart.setOption({
                  series : [ {
                    // 根据名字对应到相应的数据
                    data : gao
                  } ]
                });

              }

            },
            error : function(errorMsg) {
              //请求失败时执行该函数
              alert("图表请求数据失败!");
              myChart.hideLoading();
            }
          })






        </script>
      </div>
      <div class="ly-triangle21"></div>
    </div>
  <!--试验-->

  <ul class="bg-bubbles">
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
    <li><img src="img/logol.png" style="width:100%;height:100%"></li>
  </ul>

</div>

<audio src="music/1.mp3" autoplay="autoplay" loop="loop" />
<script src="js/jquery-1.8.3.min.js"></script>
<script src="js/jquery.fullPage.min.js"></script>
<script src="js/diy.js"></script>
</body>
</html>
蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

浅入 React 生命周期相关(二)更新生命周期

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里


更新阶段分为两部分 父组件执行 render 或者调用 this.setState。

componentWillReceiveProps
大部分网上教程为外部 props 发生改变才触发 componentWillReceiveProps,其实不是,当父组件进入 render 时,无论子组件的 props 发没发生改变,都会执行该生命周期函数。 
函数参数有一个,为 nextProps,为将要新的 props。 
值得注意的是,在整个更新阶段的生命周期函数,只有在此函数内可以调用 this.setState 方法,当然其他也可以调用,但是会造成死循环 。

shouldComponentUpdate
该函数需要返回值,如没定义则默认返回 true。当返回值为 true 时,进入 componentWillIpdate ,如为 false ,则什么都不发生。所以说这是一个可以进行 React 性能优化的地方。函数参数有两个 nextProps 和 nextState。我们需用做的就是在 this.props、this.state、nextState、nextProps之间进行对比,来解决重复渲染的目的。

componentWillUpdate
如果 shouldComponentUpdate 返回值为 true 的话,生命周期会进入该函数中。在这个函数中我们可以根据实际情况做一些事情,但是不能调用 this.setState。

render
在更新阶段的 render 来讲一讲 调和 过程。 render 返回的 JSX 标签会保存在内存中,react 会通过 diff 算法来计算出最小化改动完成差异的更新。diff 是逐层递归比较,首先比较类型是否一样。如果发现 <div>和 <span> 的差别的话,react 会选择直接放弃之前的 dom 元素, 重新渲染。所以说即使是更新阶段的调和过程,也会触发组件的挂载、卸载阶段。

componentDidUpdate
在这个时候已经更新完 dom 结构,可以重新使用 dom 操作。

总结
总体来说更新的生命周期要做的最重要的事情就是性能优化,减少重复渲染次数。 
在这个方面已经有很多成熟的解决方法了,在我的博客中也会介绍如何定制更新阶段的生命周期函数。 
在使用上,最最重要的一点就是不要在除了 componentWillReceiveProps 之外的其他更新阶段生命周期函数内调用 this.setState。

相关链接:

浅入 React 生命周期相关(一)挂载生命周期
--------------------- 

重新学习 React (一) 生命周期,Fiber 调度和更新机制

seo达人

如果您想订阅本博客内容,每天自动发到您的邮箱中, 请点这里

前几天面试问道 react 的相关知识,对我打击比较大,感觉对 react 认识非常肤浅,所以在这里重新梳理一下,想想之前没有仔细思考过的东西。

另外有说的不对的地方还请帮我指正一下,先谢谢各位啦。

目录索引:

什么是生命周期和调度?

React 有一套合理的运行机制去控制程序在指定的时刻该做什么事,当一个生命周期钩子被触发后,紧接着会有下一个钩子,直到整个生命周期结束。

生命周期

生命周期代表着每个执行阶段,比如组件初始化,更新完成,马上要卸载等等,React 会在指定的时机执行相关的生命周期钩子,使我们可以有机在程序运行中会插入自己的逻辑。

调度

我们写代码的时候往往会有很多组件以及他们的子组件,各自调用不同的生命周期,这时就要解决谁先谁后的问题,在 react v16 之前是采用了递归调用的方式一个一个执行,而在现在 v16 的版本中则采用了与之完全不同的处理(调度)方式,名叫 Fiber,这个东西 facebook 做了有两年时间,实现非常复杂。

具体 Fiber 它是一个什么东西呢?不要着急,我们先从最基本的生命周期钩子看起。

React 生命周期详解

首先看一下 React V16.4 后的生命周期概况(图片来源

 

 

  • 从横向看,react 分为三个阶段:
    • 创建时
      • constructor() - 类构造器初始化
      • static getDerivedStateFromProps() - 组件初始化时主动触发
      • render() - 递归生成虚拟 DOM
      • componentDidMount() - 完成首次 DOM 渲染
    • 更新时
      • static getDerivedStateFromProps() - 每次 render() 之前执行
      • shouldComponentUpdate() - 校验是否需要执行更新操作
      • render() - 递归生成虚拟 DOM
      • getSnapshotBeforeUpdate() - 在渲染真实 DOM 之前
      • componentDidUpdate() - 完成 DOM 渲染
    • 卸载时
      • componentWillUnmount() - 组件销毁之前被直接调用

一些干货

  • 有三种方式可以触发 React 更新,props 发生改变,调用 setState() 和调用 forceUpdate()
  • static getDerivedStateFromProps() 这个钩子会在每个更新操作之前(即使props没有改变)执行一次,使用时应该保持谨慎。
  • componentDidMount() 和 componentDidUpdate() 执行的时机是差不多的,都在 render 之后,只不过前者只在首次渲染后执行,后者首次渲染不会执行
  • getSnapshotBeforeUpdate() 执行时可以获得只读的新 DOM 树,此函数的返回值为 componentDidUpdate(prevProps, prevState, snapshot) 的第三个参数

尝试理解 Fiber

关于 Fiber,强烈建议听一下知乎上程墨Morgan的 live 《深入理解React v16 新功能》,这里潜水员的例子和图片也是引用于此 live。

背景

我们知道 React 是通过递归的方式来渲染组件的,在 V16 版本之前的版本里,当一个状态发生变更时,react 会从当前组件开始,依次递归调用所有的子组件生命周期钩子,而且这个过程是同步执行的且无法中断的,一旦有很深很深的组件嵌套,就会造成严重的页面卡顿,影响用户体验。

React 在V16版本之前的版本里引入了 Fiber 这样一个东西,它的英文涵义为纤维,在计算机领域它排在在进程和线程的后面,虽然 React 的 Fiber 和计算机调度里的概念不一样,但是可以方便对比理解,我们大概可以想象到 Fiber 可能是一个比线程还短的时间片段。

Fiber 到底做了什么事

Fiber 把当前需要执行的任务分成一个个微任务,安排优先级,然后依次处理,每过一段时间(非常短,毫秒级)就会暂停当前的任务,查看有没有优先级较高的任务,然后暂停(也可能会完全放弃)掉之前的执行结果,跳出到下一个微任务。同时 Fiber 还做了一些优化,可以保持住之前运行的结果以到达复用目的。

举个潜水员的例子

我们可以把调度当成一个潜水员在海底寻宝,v16 之前是通过组件递归的方式进行寻宝,从父组件开始一层一层深入到最里面的子组件,也就是如下图所示。

 

 

 

而替换成了 Fiber 后,海底变成的狭缝(简单理解为递归变成了遍历),潜水员会每隔一小段时间浮出水面,看看有没有其他寻宝任务。注意此时没有寻到宝藏的话,那么之前潜水的时间就浪费了。就这样潜水员会一直下潜和冒泡,具体如下图所示。

 

 

 

引入 Fiber 后带来的三个阶段

从生命周期那张图片纵向来看,Fiber 将整个生命周期分成了三个阶段:

  • render 阶段
    • 由于 Fiber 会时不时跳出任务,然后重新执行,会导致该阶段的生命周期调用多次的现象,所以 React V16 之前 componentWillMount()componentWillUpdate()componentWillReceiveProps() 的三个生命周期钩子被加上了 UNSAFE 标记
    • 这个阶段效率不一定会比之前同步递归来的快,因为会有任务跳出重做的性能损耗,但是从宏观上看,它不断执行了最高优先级(影响用户使用体验)的任务,所以用户使用起来会比以前更加的流畅
    • 这个阶段的生命周期钩子可能会重复调用,建议只写无副作用的代码
  • pre-commit 阶段
    • 该阶段 DOM 已经形成,但还是只读状态
    • 这个阶段组件状态不会再改变
  • commit 阶段
    • 此时的 DOM 可以进行操作
    • 这个阶段组件已经完成更新,可以写一些有副作用的代码和添加其它更新操作。

简而言之:以 render() 为界,之前执行的生命周期都有可能会打断并多次调用,之后的生命周期是不可被打断的且只会调用一次。所以尽量把副作用的代码放在只会执行一次的 commit 阶段。

其它生命周期钩子

除了上面常用的钩子,React 还提供了如下钩子:

  • static getDerivedStateFromError() 在 render 阶段执行,通过返回 state 更新组件状态
  • componentDidCatch() 在 commit 阶段执行,可以放一些有副作用的代码

更新机制

理解了生命周期和三个执行阶段,就可以比较容易理解组件状态的更新机制了。

setState()

这个方法可以让我们更新组件的 state 状态。第一个参数可以是对象,也可以是 updater 函数,如果是函数,则会接受当前的 state 和 props 作为参数。第二个参数为函数,是在 commit 阶段后执行,准确的说是在 componentDidUpdate() 后执行。

setState() 的更新过程是异步的(除非绑定在 DOM 事件中或写在 setTimeout 里),而且会在最后合并所有的更新,如下:

Object.assign( previousState,
  {quantity: state.quantity + 1},
  {quantity: state.quantity + 1},
  ...
)
复制代码

之所以设计成这样,是为了避免在一次生命周期中出现多次的重渲染,影响页面性能。

forceUpdate()

如果我们想强制刷新一个组件,可以直接调用该方法,调用时会直接执行 render() 这个函数而跳过 shouldComponentUpdate()

举个极端例子

function wait() { return new Promise(resolve => {
    setTimeout(() => {
      resolve(); console.log("wait");
    }, 0);
  });
} //......省略组件创建 async componentDidMount() { await wait(); this.setState({ name: "new name" }); console.log("componentDidMount");
}

componentDidUpdate() { console.log("componentDidUpdate");
}

render() { console.log(this.state); return null } //......省略组件创建 // 输出结果如下 // wait // {name: "new name"} // componentDidUpdate // componentDidMount // 注意 componentDidUpdate 的输出位置,一般情况下 // componentDidUpdate 都是在componentDidMount 后面 // 执行的,但是这里因为setState 写在了 await 后面 // 所以情况相反。 复制代码

结语

了解 react 生命周期和更新机制确实有利于编写代码,特别是当代码量越来越大时,错用的 setState 或生命周期钩子都可能埋下越来越多的雷,直到有一天无法维护。。。

我的个人建议如下:

  • 把副作用代码通通放在 commit 阶段,因为这个阶段不会影响页面渲染性能
  • 尽可能不要使用 forceUpdate() 方法,借用 Evan You 的一句话,如果你发现你自己需要在 Vue 中做一次强制更新,99.9% 的情况,是你在某个地方做错了事
  • 只要调用了 setState() 就会进行 render(),无论 state 是否改变
  • 知道 setState() 更新的什么时候是同步的,什么时候是异步的,参见上文
  • 不要把 getDerivedStateFromProps() 当成是 UNSAFE_componentWillReceiveProps() 的替代品,因为 getDerivedStateFromProps() 会在每次 render() 之前执行,即使 props 没有改变




蓝蓝设计www.lanlanwork.com )是一家专注而深入的界面设计公司,为期望卓越的国内外企业提供卓越的UI界面设计、BS界面设计 、 cs界面设计 、 ipad界面设计 、 包装设计 、 图标定制 、 用户体验 、交互设计、 网站建设 平面设计服务

日历

链接

blogger

蓝蓝 http://www.lanlanwork.com

存档