移动端横屏方案

文章转载于掘金 https://juejin.im/entry/5a30a76df265da430703411e

js实现手机横竖屏事件

在做H5项目时,需要在横竖屏变化时,做一些处理。毫无疑问,需要使用orientationchange来监听横竖屏的变化。

方案1 orientationchange事件

1
2
3
4
// 监听 orientation changes
window.addEventListener("orientationchange", function(event) {
// 根据event.orientation|screen.orientation.angle等于0|180、90|-90度来判断横竖屏
}, false);

orientationchange事件在低端的adroid机器上存在兼容性问题,可能不会触发

方案2 resize配合(window.inner/outerWidth, window.inner/outerHeight)

1
2
3
4
5
6
7
8
window.addEventListener("resize", function(event) {
truevar orientation=(window.innerWidth > window.innerHeight)? "landscape":"portrait";
trueif(oritentation === 'portrait'){
truetrue// do something ……
true} else {
truetrue// do something else ……
true}
}, false);

缺点

只要windowsize变化,就会不断触发触发resize事件。可以使用setTimeout来优化一下
如果有多个地方需要监听横竖屏,就需要注册多个window.addEventListener("resize", function(event) {……})

能不能通过订阅与发布模式来改进一下,只注册一个resize负责监听横竖屏变化,只要横竖发生变化就发布通知订阅的对象。其他需要监听横竖屏的地方只需订阅一下即可。

关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var resizeCB = function(){
if(win.innerWidth > win.innerHeight){//初始化判断
meta.init = 'landscape';
meta.current = 'landscape';
} else {
meta.init = 'portrait';
meta.current = 'portrait';
}
return function(){
true if(win.innerWidth > win.innerHeight){
truetrue if(meta.current !== 'landscape'){
truetruetrue meta.current = 'landscape';
truetruetrue event.trigger('__orientationChange__', meta);
truetrue }
true } else {
truetrue if(meta.current !== 'portrait'){
truetruetrue meta.current = 'portrait';
truetruetrue event.trigger('__orientationChange__', meta);
truetrue }
true }
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* 横竖屏
* @param {Object}
*/
function changeOrientation($print) {
var width = document.documentElement.clientWidth;
var height = document.documentElement.clientHeight;
if(width < height) {
true $print.width(height);
true $print.height(width);
true $print.css('top', (height - width) / 2 );
true $print.css('left', 0 - (height - width) / 2 );
true $print.css('transform', 'rotate(90deg)');
true $print.css('transform-origin', '50% 50%');
}
var evt = "onorientationchange" in window ? "orientationchange" : "resize";
window.addEventListener(evt, function() {
true setTimeout(function() {
true var width = document.documentElement.clientWidth;
true var height = document.documentElement.clientHeight;
true // 刷新城市的宽度
true initCityWidth();
true // 初始化每个气泡和自行车碰撞的距离
true cityCrashDistanceArr = initCityCrashDistance();
truetrueif( width > height ){
truetruetrue$print.width(width);
truetruetrue$print.height(height);
truetruetrue$print.css('top', 0 );
truetruetrue$print.css('left', 0 );
truetruetrue$print.css('transform' , 'none');
truetruetrue$print.css('transform-origin' , '50% 50%');
truetrue }
truetrue else {
truetrue $print.width(height);
truetruetrue$print.height(width);
truetruetrue$print.css('top', (height-width)/2 );
truetruetrue$print.css('left', 0-(height-width)/2 );
truetruetrue$print.css('transform' , 'rotate(90deg)');
truetruetrue$print.css('transform-origin' , '50% 50%');
truetrue }
true}, 300);
}, false);
}

改进方案1

基于CSS3@media媒体查询检测来实现

通过window.innerWidth > window.innerHeight来实现的是一种伪检测,有点不可靠。 可不可以通过浏览器来实现检测?如基于CSS3@media媒体查询来实现。

实现思路:
  • 创建包含标识横竖屏状态的特定css样式
  • 通过JS向页面中注入CSS代码
  • resize回调函数中获取横竖屏的状态

这里选择的节点font-family作为检测样式属性。
理由如下:

  1. 选择主要为了避免reflow和repaint
  2. 选择font-family样式,主要是因为font-family有如下特性:

        优先使用排在前面的字体。

        如果找不到该种字体,或者该种字体不包括所要渲染的文字,则使用下一种字体。

        如果所列出的字体,都无法满足需要,则让操作系统自行决定使用哪种字体。
    这样我们就可以指定特定标识来标识横竖屏的状态,不过需要将指定的标识放置在其他字体的前面,这样就不会引起hmtl字体的变化。
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
// callback
var resizeCB = function() {
// 取到html标签原有的样式
var hstyle = win.getComputedStyle(html, null),
ffstr = hstyle['font-family'],
pstr = "portrait, " + ffstr,
lstr = "landscape, " + ffstr,
// 拼接css
cssstr = '@media (orientation: portrait) { .orientation{font-family:' + pstr + ';} } @media (orientation: landscape) { .orientation{font-family:' + lstr + ';}}';
// 载入样式
loadStyleString(cssstr);
// 添加类
html.className = 'orientation' + html.className;
if (hstyle['font-family'] === pstr) { //初始化判断
meta.init = 'portrait';
meta.current = 'portrait';
} else {
meta.init = 'landscape';
meta.current = 'landscape';
}
return function() {
if (hstyle['font-family'] === pstr) {
if (meta.current !== 'portrait') {
meta.current = 'portrait';
event.trigger('__orientationChange__', meta);
}
} else {
if (meta.current !== 'landscape') {
meta.current = 'landscape';
event.trigger('__orientationChange__', meta);
}
}
}
}();

代码

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
/**
* pl library : orientation detection
* 横竖屏检测库
*/
;
(function(win, pl) {
var meta = {},
cbs = [],
timer;
var html = document.documentElement;
// 订阅与发布
var event = function() {
var fnlist = {},
listen,
trigger,
remove;
listen = function(e, fn) {
if (!fnlist[e]) {
fnlist[e] = [];
}
fnlist[e].push(fn);
};
trigger = function(e) {
var key = [].shift.call(arguments),
fns = fnlist[key];
if (!fns || fns.length === 0) {
return false;
}
for (var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
};
remove = function(e, fn) {
var fns = fnlist[e];
if (!fns) {
return false;
}
if (!fn) {
fns && (fns.length = 0);
} else {
for (var j = 0, l = fns.length; j < l; j--) {
if (fn === fns[j]) {
fns.splice(j, 1);
}
}
}
}
return {
listen: listen,
trigger: trigger,
remove: remove
}
}();
// automatically load css script
function loadStyleString(css) {
var _style = document.createElement('style'),
_head = document.head ? document.head : document.getElementsByTagName('head')[0];
_style.type = 'text/css';
try {
_style.appendChild(document.createTextNode(css));
} catch (ex) {
// lower IE support, if you want to know more about this to see http://www.quirksmode.org/dom/w3c_css.html
_style.styleSheet.cssText = css;
}
_head.appendChild(_style);
return _style;
}
// callback
var resizeCB = function() {
var hstyle = win.getComputedStyle(html, null),
ffstr = hstyle['font-family'],
pstr = "portrait, " + ffstr,
lstr = "landscape, " + ffstr,
cssstr = '@media (orientation: portrait) { .orientation{font-family:' + pstr + ';} } @media (orientation: landscape) { .orientation{font-family:' + lstr + ';}}';
meta.font = ffstr;
// 载入样式
loadStyleString(cssstr);
// 添加类
html.className = 'orientation' + html.className;
if (hstyle['font-family'] === pstr) { //初始化判断
meta.init = 'portrait';
meta.current = 'portrait';
} else {
meta.init = 'landscape';
meta.current = 'landscape';
}
return function() {
if (hstyle['font-family'] === pstr) {
if (meta.current !== 'portrait') {
meta.current = 'portrait';
event.trigger('__orientationChange__', meta);
}
} else {
if (meta.current !== 'landscape') {
meta.current = 'landscape';
event.trigger('__orientationChange__', meta);
}
}
}
}();
// 监听
win.addEventListener('resize', function() {
timer && win.clearTimeout(timer);
timer = win.setTimeout(resizeCB, 300);
}, false);
event.listen('__orientationChange__', function(event) {
if (cbs.length === 0) {
return false;
}
for (var i = 0, cb; cb = cbs[i++];) {
if (typeof cb === 'function') {
cb.call(pl, event);
} else {
throw new Error('The accepted argument of pl.on must be a function.');
}
}
});
// 接口
pl.orientation = meta;
pl.event = event;
pl.on = function(cb) {
cbs.push(cb);
}
})(window, window['pl'] || (window['pl'] = {}));

改进方案2

可以再改进一下,在支持orientationchange时,就使用原生的orientationchange,不支持则使用上面方案

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
// 是否支持orientationchange事件
var isOrientation = ('orientation' in window && 'onorientationchange' in window);
// callback
var orientationCB = function(e) {
if (win.orientation === 180 || win.orientation === 0) {
meta.init = 'portrait';
meta.current = 'portrait';
}
if (win.orientation === 90 || win.orientation === -90) {
meta.init = 'landscape';
meta.current = 'landscape';
}
return function() {
if (win.orientation === 180 || win.orientation === 0) {
meta.current = 'portrait';
}
if (win.orientation === 90 || win.orientation === -90) {
meta.current = 'landscape';
}
event.trigger(eventType, meta);
}
};
var callback = isOrientation ? orientationCB() : (function() {
resizeCB();
return function() {
timer && win.clearTimeout(timer);
timer = win.setTimeout(resizeCB, 300);
}
})();
// 监听
win.addEventListener(isOrientation ? eventType : 'resize', callback, false);

完整代码

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
* pl library : orientation detection
* 横竖屏检测库
*/
;
(function(win, pl) {
var meta = {},
cbs = [],
timer;
var eventType = 'orientationchange';
// 是否支持orientationchange事件
var isOrientation = ('orientation' in window && 'onorientationchange' in window);
meta.isOrientation = isOrientation;
// font-family
var html = document.documentElement,
hstyle = win.getComputedStyle(html, null),
ffstr = hstyle['font-family'];
meta.font = ffstr;
// 订阅与发布
var event = function() {
var fnlist = {},
listen,
trigger,
remove;
listen = function(e, fn) {
if (!fnlist[e]) {
fnlist[e] = [];
}
fnlist[e].push(fn);
};
trigger = function(e) {
var key = [].shift.call(arguments),
fns = fnlist[key];
if (!fns || fns.length === 0) {
return false;
}
for (var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments);
}
};
remove = function(e, fn) {
var fns = fnlist[e];
if (!fns) {
return false;
}
if (!fn) {
fns && (fns.length = 0);
} else {
for (var j = 0, l = fns.length; j < l; j--) {
if (fn === fns[j]) {
fns.splice(j, 1);
}
}
}
}
return {
listen: listen,
trigger: trigger,
remove: remove
}
}();
// automatically load css script
function loadStyleString(css) {
var _style = document.createElement('style'),
_head = document.head ? document.head : document.getElementsByTagName('head')[0];
_style.type = 'text/css';
try {
_style.appendChild(document.createTextNode(css));
} catch (ex) {
// lower IE support, if you want to know more about this to see http://www.quirksmode.org/dom/w3c_css.html
_style.styleSheet.cssText = css;
}
_head.appendChild(_style);
return _style;
}
// callback
var orientationCB = function(e) {
if (win.orientation === 180 || win.orientation === 0) {
meta.init = 'portrait';
meta.current = 'portrait';
}
if (win.orientation === 90 || win.orientation === -90) {
meta.init = 'landscape';
meta.current = 'landscape';
}
return function() {
if (win.orientation === 180 || win.orientation === 0) {
meta.current = 'portrait';
}
if (win.orientation === 90 || win.orientation === -90) {
meta.current = 'landscape';
}
event.trigger(eventType, meta);
}
};
var resizeCB = function() {
var pstr = "portrait, " + ffstr,
lstr = "landscape, " + ffstr,
cssstr = '@media (orientation: portrait) { .orientation{font-family:' + pstr + ';} } @media (orientation: landscape) { .orientation{font-family:' + lstr + ';}}';
// 载入样式
loadStyleString(cssstr);
// 添加类
html.className = 'orientation' + html.className;
if (hstyle['font-family'] === pstr) { //初始化判断
meta.init = 'portrait';
meta.current = 'portrait';
} else {
meta.init = 'landscape';
meta.current = 'landscape';
}
resizeCB = function() {
if (hstyle['font-family'] === pstr) {
if (meta.current !== 'portrait') {
meta.current = 'portrait';
event.trigger(eventType, meta);
}
} else {
if (meta.current !== 'landscape') {
meta.current = 'landscape';
event.trigger(eventType, meta);
}
}
}
};
var callback = isOrientation ? orientationCB() : (function() {
resizeCB();
return function() {
timer && win.clearTimeout(timer);
timer = win.setTimeout(resizeCB, 300);
}
})();
// 监听
win.addEventListener(isOrientation ? eventType : 'resize', callback, false);
event.listen(eventType, function(event) {
if (cbs.length === 0) {
return false;
}
for (var i = 0, cb; cb = cbs[i++];) {
if (typeof cb === 'function') {
cb.call(pl, event);
} else {
throw new Error('The accepted argument of pl.on must be a function.');
}
}
});
// 接口
pl.neworientation = meta;
pl.event = event;
pl.on = event.listen;
pl.add = function(cb) {
cbs.push(cb);
}
})(window, window['pl'] || (window['pl'] = {}));

改进方案3

目前,上述几种方案都是通过自定制的订阅与发布事件模式来实现的。这里可以基于浏览器的事件机制,来模拟orientationchange。即对orientationchange的不兼容进行修复。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var eventType = 'orientationchange';
// 触发原生orientationchange
var fire = function() {
var e;
if (document.createEvent) {
e = document.createEvent('HTMLEvents');
e.initEvent(eventType, true, false);
win.dispatchEvent(e);
} else {
e = document.createEventObject();
e.eventType = eventType;
if (win[eventType]) {
win[eventType]();
} else if (win['on' + eventType]) {
win['on' + eventType]();
} else {
win.fireEvent(eventType, e);
}
}
}

代码

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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/**
* orientationchange-fix
* orientationchange修复实用库
*/
;
(function(win) {
var meta = {},
timer;
var eventType = 'orientationchange';
// 是否支持orientationchange事件
var isOrientation = ('orientation' in window && 'onorientationchange' in window);
meta.isOrientation = isOrientation;
// font-family
var html = document.documentElement,
hstyle = win.getComputedStyle(html, null),
ffstr = hstyle['font-family'];
meta.font = ffstr;
// automatically load css script
function loadStyleString(css) {
var _style = document.createElement('style'),
_head = document.head ? document.head : document.getElementsByTagName('head')[0];
_style.type = 'text/css';
try {
_style.appendChild(document.createTextNode(css));
} catch (ex) {
// lower IE support, if you want to know more about this to see http://www.quirksmode.org/dom/w3c_css.html
_style.styleSheet.cssText = css;
}
_head.appendChild(_style);
return _style;
}
// 触发原生orientationchange
var isSupportCustomEvent = window.CustomEvent ? true : false,
fireEvent;
// https://github.com/krambuhl/custom-event-polyfill/blob/master/custom-event-polyfill.js
// Polyfill for creating CustomEvents on IE9/10/11
if (isSupportCustomEvent) {
try {
var ce = new window.CustomEvent('test');
ce.preventDefault();
if (ce.defaultPrevented !== true) {
// IE has problems with .preventDefault() on custom events
// http://stackoverflow.com/questions/23349191
throw new Error('Could not prevent default');
}
} catch (e) {
var CustomEvent = function(event, params) {
var evt, origPrevent;
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
};
evt = document.createEvent("CustomEvent");
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
origPrevent = evt.preventDefault;
evt.preventDefault = function() {
origPrevent.call(this);
try {
Object.defineProperty(this, 'defaultPrevented', {
get: function() {
return true;
}
});
} catch (e) {
this.defaultPrevented = true;
}
};
return evt;
};
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent; // expose definition to window
}
}
fireEvent = isSupportCustomEvent ? function(element, eventName, params) {
var evt = document.createEvent('CustomEvent');
if (params) {
evt.initCustomEvent(eventName, params.bubbles, params.cancelable, params.detail);
} else {
evt.initCustomEvent(eventName, false, false, void(0));
}
if (element.dispatchEvent) {
element.dispatchEvent(evt);
}
return evt;
} : function(element, eventName, params) {
var evt = document.createEventObject();
evt.type = eventName;
if (params) {
evt.bubbles = Boolean(params.bubbles);
evt.cancelable = Boolean(params.cancelable);
evt.detail = params.detail;
} else {
evt.bubbles = false;
evt.cancelable = false;
evt.detail = void(0);
}
// fire
if (element[eventName]) {
element[eventName](evt);
} else if (element['on' + eventName]) {
element['on' + eventName](evt);
} else if (element.fireEvent && ('on' + eventName) in element) { //针对IE8及以下版本,fireEvent|attachEvent|detachEvent只能使用如下事件名
element.fireEvent('on' + eventName, evt);
}
return evt;
};
// callback
var orientationCB = function(e) {
if (win.orientation === 180 || win.orientation === 0) {
meta.init = 'portrait';
meta.current = 'portrait';
}
if (win.orientation === 90 || win.orientation === -90) {
meta.init = 'landscape';
meta.current = 'landscape';
}
return function() {
if (win.orientation === 180 || win.orientation === 0) {
meta.current = 'portrait';
}
if (win.orientation === 90 || win.orientation === -90) {
meta.current = 'landscape';
}
fireEvent(window, eventType);
}
};
var resizeCB = function() {
var pstr = "portrait, " + ffstr,
lstr = "landscape, " + ffstr,
cssstr = '@media (orientation: portrait) { .orientation{font-family:' + pstr + ';} } @media (orientation: landscape) { .orientation{font-family:' + lstr + ';}}';
// 载入样式
loadStyleString(cssstr);
// 添加类
html.className = 'orientation ' + html.className;
if (hstyle['font-family'] === pstr) { //初始化判断
meta.init = 'portrait';
meta.current = 'portrait';
} else {
meta.init = 'landscape';
meta.current = 'landscape';
}
resizeCB = function() {
if (hstyle['font-family'] === pstr) {
if (meta.current !== 'portrait') {
meta.current = 'portrait';
fireEvent(window, eventType);
}
} else {
if (meta.current !== 'landscape') {
meta.current = 'landscape';
fireEvent(window, eventType);
}
}
}
};
var callback = isOrientation ? orientationCB() : (function() {
resizeCB();
return function() {
timer && win.clearTimeout(timer);
timer = win.setTimeout(resizeCB, 300);
}
})();
// 监听
win.addEventListener(isOrientation ? 'orientationchange' : 'resize', callback, false);
win.neworientation = meta;
})(window);

方案3 matchMedia

css3为我们提供了很强大的media query,而我们时常需要在js中动态的知道什么时候某个状态满足了。CSS Object Model(CSSOM)Views规范增加了对JavaScript操作CSS media query的原生支持,它在window对象下增加了matchMedia()方法。

MediaQueryList对象

你可以传入一个CSS media query然后返回一个MediaQueryList对象。这个对象包括两个属性:matches,布尔值数据,表示CSS media query是否与当前的显示状态匹配;media对应传入的参数字符串。如下:

1
2
3
var mediaQueryList = window.matchMedia("screen and (max-width:480px)");
console.log(match.media); //"screen and (max-width:480px)"
console.log(match.matches); //true or false

MediaQueryList对象监听器

1
2
3
4
5
6
7
var match = window.matchMedia("(orientation:portrait)");
match.addListener(function(mql){
trueif (match.matches) {
true}else {
}
});

当视图状态发生改变时,监听器对应的函数就会执行,而对应的MediaQueryList对象也会传入。用这个方式吗,你可以让你的JavaScript可以很快地响应布局变化,并且不需要用轮询的方式。另外关于media query的实现原理一直不太清楚,什么时候media query就生效的,比如说转屏,是否是只要当前屏幕width或height发生改变时就去查询media query估计就待看看webkit源码才能清楚了。

原生的matchMedia有些问题; GMU在遵照CSS Object Model(CSSOM)Views规范,在zepto基础上扩展实现了$.matchMedia方法,该方法返回一个对象,该对象包含matches(是否match query),当前查询query, addListener和removeListener,调用方式与原生window.matchMedia一致。

具体思路:

(1)为页面添加检测元素

css media query主要还是在style上起作用,故在页面创建一个div,作为media query作用的对象。

1
$mediaElem = $('<div class="' + cls + '" id="' + id + '"></div>').appendTo('body')
(2)为检测元素添加transition样式及media query样式

当query条件满足时,去动态修改transition作用的属性,如(width),则可触发transitionEnd事件,这样则相当于可以监测到media query。

1
2
3
$style = $('<style></style>').append('.' + cls + '{' + cssPrefix + 'transition: width 0.001ms; width: 0; position: absolute; top: -10000px;}\n').appendTo('head');
$style.append('@media ' + query + ' { #' + id + ' { width: 1px; } }\n')

注意下这里的query:一开始加载页面成功后为

1
2
3
$.mediaQuery = {
ortchange: 'screen and (width: ' + window.innerWidth + 'px)'
};

一开始检测元素的宽度为0,mediaQuery加载后就为1了,触发了transition动画,当有竖屏转为横屏时,mediaQuery条件就不符合了,检测元素的宽度又变回到了0,所以又会触发transition动画

注册transitionEnd事件

1
2
3
4
5
6
$mediaElem.on(transitionEnd, function() {
ret.matches = $mediaElem.width() === 1;
$.each(listeners, function (i,fn) {
$.isFunction(fn) && fn.call(ret, ret);
});
});

封装addListener及removeListener接口

主要记录在闭包中的listeners数组件,添加和删除回调函数即可

1
2
3
4
5
6
7
8
9
10
11
12
13
ret = {
matches: $mediaElem.width() === 1 ,
media: query,
addListener: function (callback) {
listeners.push(callback);
return this;
},
removeListener: function (callback) {
var index = listeners.indexOf(callback);
~index && listeners.splice(index, 1);
return this;
}
};

用$.mediaQuery实现转屏ortchange事件

实现转屏,只需将query传入检测转屏的query即可。这里在实现时,最开始遇了一点问题。最开始query值为”screen and (orientation: portrait)”,可在某个三星的机器上测试,居然键盘出来会改变视口大小,即认为是orientation改变了。后来经测试后query换成了”screen and (width: “ + window.innerWidth + “px)”,即检测设备第一次打开时的width,若转屏后width必然不满足,使得页面上的检测元素width未受media query的css影响而改变而触发transitionEnd。具体代码如下:

1
2
3
4
5
6
7
8
9
10
$(function () {
var handleOrtchange = function (mql) {
$(document.body).prepend(mql.matches);
$(window).trigger('ortchange');
};
$.mediaQuery = {
ortchange: 'screen and (width: ' + window.innerWidth + 'px)'
};
$.matchMedia($.mediaQuery.ortchange).addListener(handleOrtchange);
});

使用方法

1
2
3
$(window).on('ortchange', function () {
console.log('ortchange');
});

总结

  • 原生的orientationchange事件低端手机不支持

  • 对于不支持orientationchange的机型和系统,我们就只能借助于resize来触发,大家知道resize在很多情况下都会触发,而不光只有转屏,因此并不是很准。

  • 通常在转屏后,转屏事件不能立即触发,有转屏延迟的现象;确定当前是否已经转屏渲染完成。

参考资料

×

可以的话,我想喝杯咖啡续命

扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

文章目录
  1. 1. 文章转载于掘金 https://juejin.im/entry/5a30a76df265da430703411e
  2. 2. js实现手机横竖屏事件
    1. 2.1. 方案1 orientationchange事件
    2. 2.2. 方案2 resize配合(window.inner/outerWidth, window.inner/outerHeight)
      1. 2.2.1. 缺点
      2. 2.2.2. 代码
      3. 2.2.3. 改进方案1
        1. 2.2.3.1. 实现思路:
      4. 2.2.4. 代码
      5. 2.2.5. 改进方案2
      6. 2.2.6. 完整代码
      7. 2.2.7. 改进方案3
      8. 2.2.8. 代码
    3. 2.3. 方案3 matchMedia
      1. 2.3.1. MediaQueryList对象
      2. 2.3.2. MediaQueryList对象监听器
        1. 2.3.2.1. (1)为页面添加检测元素
        2. 2.3.2.2. (2)为检测元素添加transition样式及media query样式
    4. 2.4. 总结
    5. 2.5. 参考资料