Fei Blog

移动端Web页面适配方案

2017/11/09
阅读量:

延续之前的移动端项目开发,需求要求把从后台获取到的一段html显示在界面上。

v-html解析html文本

在Vue.js上有个指令是v-html。具体语法是:

1
<div v-html="html"></div>

其中html部分替换成要绑定的html就可以了,v-html可以解析html代码段并显示出来。
还有一个相似的指令v-text可以将以文字部分显示出来。
以上就完成了将html插入页面的操作。
但是存在着问题,插入的html是web页面格式的,显示在移动端的时候会出现横向滚动条
不符合我们项目的要求。因此,如何才能适配呢?

meta标签的viewport

通过设置html的meta标签的viewport属性。这里有详细的viewport的介绍
可以在要插入的html文本的head部分加入如下代码

1
<meta name='viewport' content='width=device-width'>

这样即可完成视口的设置,在chrome浏览器和android设备上测试,均可正常显示。
但是在iOS设备上并没有起效。查找原因,发现需要配置config文件。

iOS上设置config文件

需要把config.xml下的EnableViewportScale改为true

1
<preference name="EnableViewportScale" value="true" />

这样即可完成视口的调整,完成web页面和移动端的适配。
但是由于要插入html的页面还有其他内容,大体的结构是

1
2
3
<div id='div1'></div>
<div v-html='html'></div>
<div id='div2'></div>

在设置了viewport之后会导致整个页面缩放的效果,从而导致div1和div2的内容也进行了缩放,不符合我们的要求。

iframe嵌入html文本

考虑到使用iframe定义一个内联框架,可以用来在当前html文档中嵌入另一个文档。具体语法为

1
2
3
4
5
<div id='iframeWrapper'>
<iframe :srcdoc='content'></iframe>
或者
<iframe src='content.html'></iframe>
</div>

通过设定iframe的src可以完成嵌入html的操作。在chrome和android设备上测试通过,
但是在iOS设备上,iframe不能正常显示。同样,需要配置config文件。

iOS上设置config文件

查阅资料得知,需要在config.xml里配置权限

1
2
3
<allow-navigation href="*"/>
<allow-intent href="*"/>
<access origin="*" />

配置完成之后,iOS设备也可显示iframe,接下来要设置iframe的格式了。

iframe动态适配高度和宽度

承接上面的问题,嵌入的html文本是带格式的,我们需要动态设置iframe的高度和宽度,使得它的宽度正好为设备的宽度,不再出现横向滚动条。
具体做法为在iframe的onload事件中设置样式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
onloaded: function () {
var iframe = document.getElementById('iframe')
var height = iframe.contentWindow.document.body.scrollHeight || iframe.contentWindow.document.documentElement.scrollHeight
var width = iframe.contentWindow.document.body.scrollWidth || iframe.contentWindow.document.documentElement.scrollWidth
iframe.height = height
iframe.width = width
iframe.style.minWidth = width
iframe.style.minHeight = height
var clientwidth = document.documentElement.clientWidth
var scale = clientwidth / width
iframe.style.transform = 'scale(' + scale + ')'
iframe.style.transformOrigin = '0 0'
iframe.style.border = 'none'
}

如上面代码所示,可以通过设置transform的scale属性完成缩放的操作,在缩放之后需要把transformOrigin属性(默认50% 50%)设置一下,以上完成了缩放的操作。
但是发现scale虽然缩放了,但是并没有影响布局,元素还占用了之前的空间,这里有说明,
导致页面中间有一段空白。
因此,需要动态调整iframe外层的div元素的高度,具体代码示例如下

1
window.parent.document.getElementById('iframeWrapper').style.height = height * scale + 'px'

这样可以实现iframe和父元素的高度随着插入html的高度自适应变化。

onload函数调用了两次

测试过程中发现,onload函数调用了两次。调整代码结构,将html文本中的iframe标签去掉,通过js来动态增加iframe元素。完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
onLoaded: function (content) {
var ifrWrapper = document.getElementById('iframeWrapper')
var iframe = document.createElement('iframe')
iframe.onload = function () {
var clientwidth = document.documentElement.clientWidth
var height = iframe.contentWindow.document.body.scrollHeight || iframe.contentWindow.document.documentElement.scrollHeight
var width = iframe.contentWindow.document.body.scrollWidth || iframe.contentWindow.document.documentElement.scrollWidth
iframe.height = height
iframe.width = width
iframe.style.minWidth = width
iframe.style.minHeight = height
var scale = clientwidth / width
iframe.style.transform = 'scale(' + scale + ')'
iframe.style.transformOrigin = '0 0'
iframe.style.border = 'none'
window.parent.document.getElementById('iframeWrapper').style.height = height * scale + 'px'
}
iframe.srcdoc = content
ifrWrapper.appendChild(iframe)
}

以上的函数在从后台获取到要插入的html的内容的时候调用。
至此,通过iframe插入html文本并动态设置高度和宽度的功能就完成了。
但是页面调整到宽度跟设备宽度一致之后,字号会变小,需求希望用户可以自己手势控制放大。之前想通过设置viewport的user-scalable为yes来允许用户自行缩放,

1
<meta name="viewport" content="user-scalable=yes, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width">

但是在子页面设置了user-scalable之后会导致整个app的所有页面都可以用户缩放,不符合要求,
希望设置一个开关来完成user-scalable的yes或no,但是没有用,这里有详细的介绍
只能寻求其他的解决方案。

AlloyFinger实现手势缩放

承接以上的问题,在实现缩小之后,字号会变小,需求要求在这个页面可以手势放大(pinch事件).
采用腾讯的AlloyFinger来控制用户两指缩放。
使用方式,首先安装

1
npm install alloyfinger --save

之后在页面引入

1
import AlloyFinger from 'alloyfinger'

然后在components中使用AlloyFinge。至此可以使用v-finger来实现手势的识别了。
参考代码

1
2
3
4
5
6
7
8
9
10
11
12
13
var pinchRotateImg = document.getElementById("pinchRotateImg");
Transform(pinchRotateImg);
new AlloyFinger(pinchRotateImg, {
rotate:function(evt){
pinchRotateImg.rotateZ += evt.angle;
},
multipointStart: function () {
initScale = pinchRotateImg.scaleX;
},
pinch: function (evt) {
pinchRotateImg.scaleX = pinchRotateImg.scaleY = initScale * evt.zoom;
}
});

在页面新加一个div的时候可以很容易的实现识别,但是在iframe上,由于需要在load完毕之后才有内容,因此,在之前的onLoaded函数里添加代码,
希望实现在只能放大,不能缩小到比原来初始化的时候小。具体完整版代码如下

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
onLoaded: function (content) {
var ifrWrapper = document.getElementById('iframeWrapper')
var iframe = document.createElement('iframe')
iframe.onload = function () {
var clientwidth = document.documentElement.clientWidth
var height = iframe.contentWindow.document.body.scrollHeight || iframe.contentWindow.document.documentElement.scrollHeight
var width = iframe.contentWindow.document.body.scrollWidth || iframe.contentWindow.document.documentElement.scrollWidth
iframe.height = height
iframe.width = width
iframe.style.minWidth = width
iframe.style.minHeight = height
var scale = clientwidth / width
iframe.style.border = 'none'
iframe.style.transform = 'scale(' + scale + ')'
iframe.style.transformOrigin = 'left top'
ifrWrapper.style.height = height * scale + 'px'
ifrWrapper.style.width = width * scale + 'px'
var initScale = scale
new AlloyFinger(iframe.contentDocument, {
multipointStart: function () {
initScale = iframe.style.transform.split('(')[1].split(')')[0]
},
pinch: function (evt) {
var newScale = initScale * evt.zoom
if (newScale < scale) {
ifrWrapper.style.overflowX = 'hidden'
iframe.style.transform = 'scale(' + scale + ')'
ifrWrapper.style.height = height * scale + 'px'
ifrWrapper.style.width = width * scale + 'px'
return
}
iframe.style.transform = 'scale(' + newScale + ')'
ifrWrapper.style.height = height * newScale + 'px'
ifrWrapper.style.width = width * newScale + 'px'
if (iframe.width > width) {
ifrWrapper.style.overflowX = 'scroll'
} else {
ifrWrapper.style.overflowX = 'hidden'
}
}
})
}
iframe.srcdoc = item.content
ifrWrapper.appendChild(iframe)
},

以上即可实现相关的功能
其他的问题还在不断完善中…

参考文献
1.https://cn.vuejs.org/v2/api/#v-html
2.https://segmentfault.com/a/1190000008767416
3.http://clfsw.iteye.com/blog/1398806
4.http://www.runoob.com/tags/tag-iframe.html
5.http://blog.csdn.net/ilv_xj/article/details/72778501
6.https://segmentfault.com/q/1010000005919829
7.https://yq.aliyun.com/ziliao/167263
8.https://zhuanlan.zhihu.com/p/25140691
9.https://github.com/AlloyTeam/AlloyFinger

CATALOG
  1. 1. v-html解析html文本
  2. 2. meta标签的viewport
    1. 2.1. iOS上设置config文件
  3. 3. iframe嵌入html文本
    1. 3.1. iOS上设置config文件
    2. 3.2. iframe动态适配高度和宽度
    3. 3.3. onload函数调用了两次
  4. 4. AlloyFinger实现手势缩放