子元素scroll父元素容器不跟随滚动JS实现

2015/12/18 · JavaScript
· 滚动

原文出处:
张鑫旭   

一、开场暖身

网上常见蹲来蹲去的小段子,比方说:“李代沫蹲,李代沫蹲,李代沫蹲完黄海波蹲;黄海波蹲,黄海波蹲,黄海波蹲完宁财神蹲;宁财神蹲,宁财神蹲,宁财神蹲完张耀扬蹲;张耀扬蹲,张耀扬蹲,张耀扬蹲完郭美美蹲;郭美美蹲,郭美美蹲,郭美美蹲完……”。应该源自“萝卜蹲,萝卜蹲,萝卜蹲完苹果蹲……”。

在网页中,滚动条的滚动行为也是类似的调调,如果页面出现多个内嵌滚动条,则行为表现是:子元素滚,子元素滚,子元素滚完父元素滚;父元素滚,父元素滚,父元素滚完容器滚……

比方说下面:

图片 1

在妹子脸上滚,先是妹子滚,妹子滚完主页面滚,对吧~

//zxx: 别问为什么不使用张含韵,因为张妹子照片是横的,滚动空间小,晓得伐~

这是浏览器的默认行为,如果我们遇到了一个需求:子元素滚,子元素滚完,就完了,父元素不需要滚了。那该如何实现呢?

在PC端,OK,本文介绍的方法,值适用于PC端,移动端,咳咳,我15年就没做过移动端项目,不好意思,手生,我也没去研究。

webgl世界 matrix入门

2017/01/18 · HTML5 ·
matrix,
WebGL

原文出处:
AlloyTeam   

这次没有带来游戏啦,本来还是打算用一个小游戏来介绍阴影,但是发现阴影这块想完完整整介绍一次太大了,涉及到很多,再加上业务时间的紧张,所以就暂时放弃了游戏,先好好介绍一遍webgl中的Matrix。

这篇文章算是webgl的基础知识,因为如果想不走马观花的说阴影的话,需要打牢一定的基础,文章中我尽力把知识点讲的更易懂。内容偏向刚上手webgl的同学,至少知道着色器是什么,webgl中drawElements这样的API会使用~

文章的标题是Matrix is
magic,矩阵对于3D世界来说确实是魔法一般的存在,说到webgl中的矩阵,PMatrix/VMatrix/MMatrix这三个大家相信不会陌生,那就正文let’s
go~

微信小程序开发07-列表页面怎么做

2018/08/10 · 基础技术 ·
小程序

原文出处: 叶小钗   

接上文: 微信小程序开发06-一个业务页面的完成

github地址:

我们首页功能基本完成,我对比了下实际工作中的需求,完成度有70%以上,如果再花一两天时间,便能跟之前工作做的差不多了,今天我们继续实现一个页面列表,便结束这次的学习,后面几天要出差,所以总结性的文章本周未必能出来,静待下周吧。

这里考虑demo复杂度,列表页功能完成度也仅仅完成主功能模块,与真实工作完成度对比60%左右吧,于是我们开始愉快的代码,首先是将我们的页面样式实现:

<view class=”page-wrapper “> <view class=”bus-list
js_bus_list “> <view data-supplierid=”100020″ data-key=””
class=”bus-list-item “> <view class=”bus-time”>
08:25</view> <view class=”tobooking”> 预订 </view>
<view class=”detail”> <view class=”detail1″> <view
class=”start”> <text class=”icon-circle
s-icon1″></text>东莞市南城汽车客运站</view> <view
class=”end”> <text class=”icon-circle
s-icon2″></text>连州</view> </view> <view
class=”tags”> <view> <text
class=”price”>¥135</text> </view> <view> <text
class=”countleft”>10张</text> </view> <view>
<text class=”b-tags js_tags”></text> </view>
</view> </view> </view> </view> <view
class=”bus-list js_bus_list “> <view data-supplierid=”100020″
data-key=”” class=”bus-list-item “> <view class=”bus-time”>
08:25</view> <view class=”tobooking”> 预订 </view>
<view class=”detail”> <view class=”detail1″> <view
class=”start”> <text class=”icon-circle
s-icon1″></text>东莞市南城汽车客运站</view> <view
class=”end”> <text class=”icon-circle
s-icon2″></text>连州</view> </view> <view
class=”tags”> <view> <text
class=”price”>¥135</text> </view> <view> <text
class=”countleft”>10张</text> </view> <view>
<text class=”b-tags js_tags”></text> </view>
</view> </view> </view> </view> <view
class=”bus-list js_bus_list “> <view data-supplierid=”100020″
data-key=”” class=”bus-list-item “> <view class=”bus-time”>
08:25</view> <view class=”tobooking”> 预订 </view>
<view class=”detail”> <view class=”detail1″> <view
class=”start”> <text class=”icon-circle
s-icon1″></text>东莞市南城汽车客运站</view> <view
class=”end”> <text class=”icon-circle
s-icon2″></text>连州</view> </view> <view
class=”tags”> <view> <text
class=”price”>¥135</text> </view> <view> <text
class=”countleft”>10张</text> </view> <view>
<text class=”b-tags js_tags”></text> </view>
</view> </view> </view> </view> <include
src=”../mod/calendar.wxml” /> <include
src=”../../utils/abstract-page.wxml” /> </view>

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<view class="page-wrapper ">
  <view class="bus-list js_bus_list ">
    <view data-supplierid="100020" data-key="" class="bus-list-item  ">
      <view class="bus-time"> 08:25</view>
      <view class="tobooking"> 预订 </view>
      <view class="detail">
        <view class="detail1">
          <view class="start">
            <text class="icon-circle s-icon1"></text>东莞市南城汽车客运站</view>
          <view class="end">
            <text class="icon-circle s-icon2"></text>连州</view>
        </view>
        <view class="tags">
          <view>
            <text class="price">¥135</text>
          </view>
          <view>
            <text class="countleft">10张</text>
          </view>
          <view>
            <text class="b-tags js_tags"></text>
          </view>
        </view>
      </view>
    </view>
  </view>
 
  <view class="bus-list js_bus_list ">
    <view data-supplierid="100020" data-key="" class="bus-list-item  ">
      <view class="bus-time"> 08:25</view>
      <view class="tobooking"> 预订 </view>
      <view class="detail">
        <view class="detail1">
          <view class="start">
            <text class="icon-circle s-icon1"></text>东莞市南城汽车客运站</view>
          <view class="end">
            <text class="icon-circle s-icon2"></text>连州</view>
        </view>
        <view class="tags">
          <view>
            <text class="price">¥135</text>
          </view>
          <view>
            <text class="countleft">10张</text>
          </view>
          <view>
            <text class="b-tags js_tags"></text>
          </view>
        </view>
      </view>
    </view>
  </view>
  <view class="bus-list js_bus_list ">
    <view data-supplierid="100020" data-key="" class="bus-list-item  ">
      <view class="bus-time"> 08:25</view>
      <view class="tobooking"> 预订 </view>
      <view class="detail">
        <view class="detail1">
          <view class="start">
            <text class="icon-circle s-icon1"></text>东莞市南城汽车客运站</view>
          <view class="end">
            <text class="icon-circle s-icon2"></text>连州</view>
        </view>
        <view class="tags">
          <view>
            <text class="price">¥135</text>
          </view>
          <view>
            <text class="countleft">10张</text>
          </view>
          <view>
            <text class="b-tags js_tags"></text>
          </view>
        </view>
      </view>
    </view>
  </view>
 
  <include src="../mod/calendar.wxml" />
  <include src="../../utils/abstract-page.wxml" />
 
</view>

CSS

.page-wrapper { margin: 0; font-size: 28rpx; line-height: 1.5; color:
#333; background-color: #efefef; overflow-x: hidden;
-webkit-overflow-scrolling: touch; -webkit-tap-highlight-color:
transparent; min-height:2000rpx; } .bus-list .bus-list-item { position:
relative; height: 160rpx; background-color: #fff; margin: 16rpx 0;
border: 2rpx solid #e5e5e5; border-width: 2rpx 0; } .bus-list
.bus-list-item::before,.bus-list .bus-list-item::after { position:
absolute; left: 122rpx; content: ”; width: 24rpx; height: 12rpx;
background-color: #efefef; border: 2rpx solid #e5e5e5; } .bus-list
.bus-list-item::before { border-radius: 0 0 60rpx 60rpx; border-top:
none; top: -2rpx; } .bus-list .bus-list-item::after { border-radius:
60rpx 60rpx 0 0; border-bottom: none; bottom: -2rpx; } .bus-list
.bus-list-item .bus-time { position: absolute; left: 0; width: 134rpx;
height: 100rpx; line-height: 100rpx; margin: 30rpx 0; color: #00b358;
text-align: center; font-size: 40rpx; font-family: Arial; border-right:
2rpx dashed #e5e5e5; } .bus-list .bus-list-item .tobooking {
background-color: #00B358; position: absolute; right: 0; width: 120rpx;
height: 160rpx; line-height: 160rpx; text-align: center; color: #fff;
font-size: 30rpx; } .bus-list .bus-list-item.disabled .tobooking {
background-color: #c5c5c5; } .bus-list .bus-list-item .detail { height:
80rpx; padding: 36rpx 0; margin: 0 140rpx 0 144rpx; } .bus-list
.bus-list-item .detail .sub-list{ height: 52rpx; } .bus-list
.bus-list-item .start, .bus-list .bus-list-item .end { color: #333333;
font-size: 26rpx; } .bus-list .bus-list-item .price { font-family:
Arial; color: #fd8f01; font-size: 32rpx; font-weight: 600; } .bus-list
.bus-list-item.disabled .ticket { display: none; } .bus-list
.bus-list-item .ticket { color: #fd8f01; font-size: 24rpx; border: 2rpx
solid #fd8f01; padding: 2rpx 8rpx; border-radius: 10rpx; font-family:
Arial; } .bus-list .bus-list-item.disabled .ticket { color: #c5c5c5; }
.bus-list .bus-list-item .s-icon1 { margin: 0 10rpx; border-color:
#00B358; } .bus-list .bus-list-item .s-icon2 { margin: 0 10rpx;
border-color: #f06463; } .bus-list .bus-list-item .tags { width:
160rpx; text-align: right; position: absolute; right: 0; margin-right:
138rpx; margin-top: 34rpx; top: 0; }

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
.page-wrapper {
    margin: 0;
    font-size: 28rpx;
    line-height: 1.5;
    color: #333;
    background-color: #efefef;
    overflow-x: hidden;
    -webkit-overflow-scrolling: touch;
    -webkit-tap-highlight-color: transparent;
    min-height:2000rpx;
}
 
.bus-list .bus-list-item {
position: relative;
height: 160rpx;
background-color: #fff;
margin: 16rpx 0;
border: 2rpx solid #e5e5e5;
border-width: 2rpx 0;
}
 
.bus-list .bus-list-item::before,.bus-list .bus-list-item::after {
position: absolute;
left: 122rpx;
content: ”;
width: 24rpx;
height: 12rpx;
background-color: #efefef;
border: 2rpx solid #e5e5e5;
}
 
.bus-list .bus-list-item::before {
  border-radius: 0 0 60rpx 60rpx;
    border-top: none;
  top: -2rpx;
}
 
.bus-list .bus-list-item::after {
  border-radius: 60rpx 60rpx 0 0;
    border-bottom: none;
  bottom: -2rpx;
}
 
.bus-list .bus-list-item .bus-time {
position: absolute;
left: 0;
width: 134rpx;
height: 100rpx;
line-height: 100rpx;
margin: 30rpx 0;
color: #00b358;
text-align: center;
font-size: 40rpx;
font-family: Arial;
border-right: 2rpx dashed #e5e5e5;
}
 
.bus-list .bus-list-item .tobooking {
background-color: #00B358;
position: absolute;
right: 0;
width: 120rpx;
height: 160rpx;
line-height: 160rpx;
text-align: center;
color: #fff;
font-size: 30rpx;
}
 
.bus-list .bus-list-item.disabled .tobooking {
  background-color: #c5c5c5;
}
 
.bus-list .bus-list-item .detail {
height: 80rpx;
padding: 36rpx 0;
margin: 0 140rpx 0 144rpx;
}
 
.bus-list .bus-list-item .detail  .sub-list{
    height: 52rpx;
}
 
 
.bus-list .bus-list-item .start, .bus-list .bus-list-item .end {
color: #333333;
font-size: 26rpx;
}
 
.bus-list .bus-list-item .price {
font-family: Arial;
color: #fd8f01;
font-size: 32rpx;
font-weight: 600;
}
 
.bus-list .bus-list-item.disabled .ticket {
display: none;
}
 
.bus-list .bus-list-item .ticket {
  color: #fd8f01;
  font-size: 24rpx;
  border: 2rpx solid #fd8f01;
  padding: 2rpx 8rpx;
  border-radius: 10rpx;
  font-family: Arial;
}
 
.bus-list .bus-list-item.disabled .ticket {
  color: #c5c5c5;
}
 
.bus-list .bus-list-item .s-icon1 {
  margin: 0 10rpx;
  border-color: #00B358;
}
 
.bus-list .bus-list-item .s-icon2 {
  margin: 0 10rpx;
  border-color: #f06463;
}
 
.bus-list .bus-list-item .tags {
    width: 160rpx;
    text-align: right;
    position: absolute;
    right: 0;
    margin-right: 138rpx;
    margin-top: 34rpx;
    top: 0;
}

轻轻松松完成了页面主体布局:

图片 2

然后这里需求请求数据,所以我们去设置一个请求实体:

class ListModel extends DemoModel { constructor() { super(); this.url =
‘/schedule/list’; } } module.exports = { cityModel: new CityModel,
city2Model: new City2Model, listModel: new ListModel }

1
2
3
4
5
6
7
8
9
10
11
12
13
class ListModel extends DemoModel {
  constructor() {
    super();
    this.url = ‘/schedule/list’;
  }
}
 
module.exports = {
  cityModel: new CityModel,
  city2Model: new City2Model,
  listModel: new ListModel
 
}

开始请求参数:

onLoad: function (data) { let scope = this; if(!data || !data.sid ||
!data.aid || !data.date) { this.showToast(‘参数错误’); return }
this.index = 0; let listModel = models.listModel; listModel.setParam({
startcityid: data.sid, arrivalcityid: data.aid, startdatetime: data.date
/ 1000, page: this.index + 1 }); this.showLoading();
listModel.execute(function(data) { scope.hideLoading();
scope._appendList(data.schedules); }); }

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
onLoad: function (data) {
    let scope = this;
 
    if(!data || !data.sid || !data.aid || !data.date) {
      this.showToast(‘参数错误’);
      return
    }
 
    this.index = 0;
    let listModel = models.listModel;
 
    listModel.setParam({
      startcityid: data.sid,
      arrivalcityid: data.aid,
      startdatetime: data.date / 1000,
      page: this.index + 1
    });
 
    this.showLoading();
    listModel.execute(function(data) {
      scope.hideLoading();
      scope._appendList(data.schedules);
 
    });
 
  }

接下来的工作便是渲染页面即可,如果不考虑细节,只是做demo,真的很轻易呢:)

//获取公共ui操作类实例 const _page =
require(‘../../utils/abstract-page.js’); let modCalendar =
require(‘../mod/calendar.js’); const models =
require(‘../../data/demo-model.js’) const util =
require(‘../../utils/util.js’) //获取应用实例 const app = getApp()
Page(_page.initPage({ data: { listData: [] }, // methods:
uiUtil.getPageMethods(), methods: { }, goIndex: function () {
wx.navigateTo({ url: ‘../index/index’ }) }, onShow: function () {
global.sss = this; let scope = this; }, _appendList: function (data) {
for(let i = 0, len = data.length; i < len; i++) { data[i].dateStr =
util.dateUtil.format(new Date(data[i].datetime * 1000), ‘H:F’ ) }
this.setData({ listData: this.data.listData.concat(data) }); }, onLoad:
function (data) { let scope = this; if(!data || !data.sid || !data.aid
|| !data.date) { this.showToast(‘参数错误’); return } this.index = 0;
let listModel = models.listModel; listModel.setParam({ startcityid:
data.sid, arrivalcityid: data.aid, startdatetime: data.date / 1000,
page: this.index + 1 }); this.showLoading();
listModel.execute(function(data) { scope.hideLoading();
scope._appendList(data.schedules); }); } }, { modCalendar: modCalendar
}))

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
//获取公共ui操作类实例
const _page = require(‘../../utils/abstract-page.js’);
let modCalendar = require(‘../mod/calendar.js’);
const models = require(‘../../data/demo-model.js’)
const util = require(‘../../utils/util.js’)
 
//获取应用实例
const app = getApp()
 
Page(_page.initPage({
  data: {
    listData: []
  },
  // methods: uiUtil.getPageMethods(),
  methods: {
  },
 
  goIndex: function () {
 
    wx.navigateTo({
      url: ‘../index/index’
    })
  },
  onShow: function () {
    global.sss = this;
    let scope = this;
  },
 
  _appendList: function (data) {
 
    for(let i = 0, len = data.length; i < len; i++) {
      data[i].dateStr = util.dateUtil.format(new Date(data[i].datetime * 1000), ‘H:F’ )
    }
 
    this.setData({
      listData: this.data.listData.concat(data)
    });
  },
 
  onLoad: function (data) {
    let scope = this;
 
    if(!data || !data.sid || !data.aid || !data.date) {
      this.showToast(‘参数错误’);
      return
    }
 
    this.index = 0;
    let listModel = models.listModel;
 
    listModel.setParam({
      startcityid: data.sid,
      arrivalcityid: data.aid,
      startdatetime: data.date / 1000,
      page: this.index + 1
    });
 
    this.showLoading();
    listModel.execute(function(data) {
      scope.hideLoading();
      scope._appendList(data.schedules);
 
    });
 
  }
}, {
  modCalendar: modCalendar
}))

<view class=”page-wrapper “> <view class=”calendar-bar-wrapper
js_calendar_wrapper”> <view class=”bus-tabs calendar-bar”>
<view class=”tabs-item js_pre_day”>前一天</view> <view
class=”tabs-item js_show_calendar” style=”-webkit-flex: 2; flex:
2;”>2018-8-6 周一(明天)</view> <view class=”tabs-item
js_next_day”>后一天</view> </view> </view>
<view class=”bus-list js_bus_list “> <block
wx:for=”{{listData}}” wx:key=”k”> <view class=”bus-list-item “>
<view class=”bus-time”>{{item.dateStr}}</view> <view
class=”tobooking”> 预订 </view> <view class=”detail”>
<view class=”detail1″> <view class=”start”> <text
class=”icon-circle
s-icon1″></text>{{item.startstationname}}</view> <view
class=”end”> <text class=”icon-circle
s-icon2″></text>{{item.arrivalstationname}}</view>
</view> <view class=”tags”> <view> <text
class=”price”>¥{{item.price / 100}}</text> </view>
<view> <text
class=”countleft”>{{item.cansellcountamount}}张</text>
</view> <view> <text class=”b-tags
js_tags”></text> </view> </view> </view>
</view> </block> </view> <include
src=”../mod/calendar.wxml” /> <include
src=”../../utils/abstract-page.wxml” /> <view class=”bus-list
js_bus_list ” ontap=”goIndex”> 去首页 </view> </view>

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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<view class="page-wrapper ">
  <view class="calendar-bar-wrapper js_calendar_wrapper">
    <view class="bus-tabs calendar-bar">
      <view class="tabs-item js_pre_day">前一天</view>
      <view class="tabs-item js_show_calendar" style="-webkit-flex: 2; flex: 2;">2018-8-6 周一(明天)</view>
      <view class="tabs-item js_next_day">后一天</view>
    </view>
  </view>
  <view class="bus-list js_bus_list ">
 
    <block wx:for="{{listData}}" wx:key="k">
      <view class="bus-list-item  ">
        <view class="bus-time">{{item.dateStr}}</view>
        <view class="tobooking"> 预订 </view>
        <view class="detail">
          <view class="detail1">
            <view class="start">
              <text class="icon-circle s-icon1"></text>{{item.startstationname}}</view>
            <view class="end">
              <text class="icon-circle s-icon2"></text>{{item.arrivalstationname}}</view>
          </view>
          <view class="tags">
            <view>
              <text class="price">¥{{item.price / 100}}</text>
            </view>
            <view>
              <text class="countleft">{{item.cansellcountamount}}张</text>
            </view>
            <view>
              <text class="b-tags js_tags"></text>
            </view>
          </view>
        </view>
      </view>
 
    </block>
 
  </view>
 
  <include src="../mod/calendar.wxml" />
  <include src="../../utils/abstract-page.wxml" />
 
 
  <view class="bus-list js_bus_list " ontap="goIndex">
    去首页
  </view>
</view>

图片 3

最后,我们完善一下这里日期相关操作,便暂时结束这次学习:

图片 4

图片 5

列表页的一些总结

我们做小程序相关学习有快两周了,完成了一个简单的demo,项目还是有一定复杂度,感觉上还是比较适合做小程序了解的,但是也有一些问题,比如写到后面事实上更多是业务的东西了,业务会涉及很多细节体验,需要耗时费力,比如今天的列表业务,显然就偷懒了,代码质量也没怎么关注,事实上大家可以思考一些问题,这里还差很多功能:

① 滚动加载

② 列表各种状态

③ ……

事实上,列表页是常用的一种业务页面,虽然各种列表页的筛选条件不一样,但是主体功能无非都是:

① 列表渲染

② 滚动加载

③ 条件筛选、重新渲染

所以说我们其实可以将其做成一个页面基类,跟abstract-page一个意思,这里留待我们下次来处理吧,借此我们微信小程序的学习教程就此结束,我后面几天总结下前面所学整理一个博客出来,帮助各位更好的了解。

1 赞 收藏
评论

图片 6

二、阻止浏览器默认行为的特定套路

哈,本文标题有些拗口,实际上用一句话概括就是:如何阻止浏览器的默认滚动行为。

基本上,好像印象中就没有例外的,阻止浏览器的默认行为,就一条(假设事件对象参数是event):event.preventDefault().

这是标准规范使用方法。但是,对于老IE浏览器,event.returnValue = false.
如果你使用jQuery等框架,直接上面的event.preventDefault()就可以,库已经帮你搞定了兼容细节处理。

OK,回到本文。阻止默认滚动,也是类似,关键是找到准确的事件。

第一反应是scroll事件,不知道是不是我测试的方法不对,结果没鸟用;其实想想也可以理解,scroll事件要触发,尼玛必须已经滚动了哈~

后来,发现要从滚动事件的源头处理起来。在PC端,绝大多数滚动都是鼠标滚动触发的(上下快捷键也可以滚动页面,但一般人不知道),因此,我们可以从鼠标滚轮事件入手。

1/ 矩阵的来源

刚刚有说到PMatrix/VMatrix/MMatrix这三个词,他们中的Matrix就是矩阵的意思,矩阵是干什么的?用来改变顶点位置信息的,先牢记这句话,然后我们先从canvas2D入手相信一下我们有一个100*100的canvas画布,然后画一个矩形

XHTML

<canvas width=”100″ height=”100″></canvas> ctx.rect(40, 40,
20, 20); ctx.fill();

1
2
3
<canvas width="100" height="100"></canvas>
ctx.rect(40, 40, 20, 20);
ctx.fill();

代码很简单,在画布中间画了一个矩形

现在我们希望将圆向左移动10px

JavaScript

ctx.rect(30, 40, 20, 20); ctx.fill();

1
2
ctx.rect(30, 40, 20, 20);
ctx.fill();

结果如下:

源码地址:
结果展示:图片 7

 

改变rect方法第一个参数就可以了,很简单,因为rect()对应的就是一个矩形,是一个对象,canvas2D是对象级别的画布操作,而webgl不是,webgl是片元级别的操作,我们操作的是顶点
用webgl如何画一个矩形?地址如下,可以直接查看源码

源码地址:
结果展示:

图片 8

这里我们可以看到position这个数组,这里面存的就是矩形4个点的顶点信息,我们可以通过操作改变其中点的值来改变位置(页面源码也可以看到实现),但是扪心自问这样不累吗?有没有可以一次性改变某个物体所有顶点的方式呢?
有,那就是矩阵,magic is coming

1  0  0  0
0  1  0  0
0  0  1  0
0  0  0  1

上面这个是一个单位矩阵(矩阵最基础的知识这里就不说了),我们用这个乘一个顶点(2,1,0)来看看
图片 9

并没有什么变化啊!那我们换一个矩阵来看

1  0  0  1
0  1  0  0
0  0  1  0
0  0  0  1

再乘之前那个顶点,发现顶点的x已经变化了!
图片 10

如果你再多用几个顶点试一下就会发现,无论我们用哪个顶点,都会得到这样的一个x坐标+1这样一个结果
来,回忆一下我们之前的目的,现在是不是有了一种一次性改变顶点位置的方式呢?

 

2/ 矩阵规律介绍
刚刚我们改变了矩阵16个值中的一个,就使得矩阵有改变顶点的能力,我们能否总结一下矩阵各个值的规律呢?当然是可以的,如下图

图片 11
这里红色的x,y,z分别对应三个方向上的偏移

图片 12
这里蓝色的x,y,z分别对应三个方向上的缩放

然后是经典的围绕各个轴的旋转矩阵(记忆的时候注意围绕y轴旋转时,几个三角函数的符号……)
图片 13

还有剪切(skew)效果的变换矩阵,这里用个x轴的例子来体现
图片 14

这里都是某一种单一效果的变化矩阵,可以相乘配合使用的,很简单。我们这里重点来找一下规律,似乎所有的操作都是围绕着红框这一块来的
图片 15
其实也比较好理解,因为矩阵这里每一行对应了个坐标
图片 16

那么问题来了,最下面那行干啥用的?
一个顶点,坐标(x,y,z),这个是在笛卡尔坐标系中的表示,在3D世界中我们会将其转换为齐次坐标系,也就是变成了(x,y,z,w),这样的形式(之前那么多图中w=1)
矩阵的最后一行也就代表着齐次坐标,那么齐次坐标有啥作用?很多书上都会说齐次坐标可以区分一个坐标是点还是向量,点的话齐次项是1,向量的话齐次项是0(所以之前图中w=1)
对于webgl中的Matrix来说齐次项有什么用处呢?或者说这个第四行改变了有什么好处呢?一句话概括(敲黑板,划重点)
它可以让物体有透视的效果
举个例子,大名鼎鼎的透视矩阵,如图
图片 17
在第四行的第三列就有值,而不像之前的是0;还有一个细节就是第四行的第四列是0,而不是之前的1

写到这里的时候我纠结了,要不要详细的把正视和透视投影矩阵推导写一下,但是考虑到篇幅,实在是不好放在这里了,否则这篇文章要太长了,因为后面还有内容
大部分3D程序开发者可能不是很关注透视矩阵(PMatrix),只是知道有这一回事,用上这个矩阵可以近大远小,然后代码上也就是glMatrix.setPerspective(……)一下就行了
所以决定后面单独再写一篇,专门说下正视透视矩阵的推导、矩阵的优化这些知识
这里就暂且打住,我们先只考虑红框部分的矩阵所带来的变化
图片 18

发表评论

电子邮件地址不会被公开。 必填项已用*标注