摘要:使用HTML5编写移动Web应用,主要是为了尝试一下“一套代码多处运行”,一个webapp几乎可以不加修改的运行在PC/Android/iOS等上面运行。但是写到现在觉得虽然这种方式弊大于利,不仅在速度上有差异,webapp对移动端的一些原生功能支持并没有那么好。我用的vue写的系统,完成之后用webpack打包模块,hbuilder打包成apk,但是要解决的问题并不少。现在来说说webapp拍照上传。

html5是支持拍照上传或者调用本地相册的,

  1. <!--兼容安卓微信调用摄像头-->
  2. <input type="file" name="file" capture="camera">
  3.  
  4. <!--兼容安卓默认选择sd卡上的相册图片-->
  5. <input type="file" name="file" accept="image/*" >

然而hbuilder打包apk之后,在安卓机(华为荣耀9)测试的时候,发现  capture="camera" 失效了,打开的是本地相册,但是不能调用摄像头。我就打开了webpack-server,移动端的浏览器运行的时候,在浏览器里面是可以调用摄像头的,最后发现了很多人都有这个问题,但是并没有说明解决办法。我自己最后是结合H5提供的 window.plus 功能调用移动端的摄像头,当然,先判断移动端是否支持 window.plus ,如果不支持,就依然用 <input> 实现图片选取。

H5 的 plus.camera 官方 API:http://www.html5plus.org/doc/zh_cn/camera.html

下面我就说说我的解决方法,主要是参照了网上一些实例和官网API写出来的,请看下面的VUE组件,这个组件可以直接引用,有兴趣的同学可以试试:

  1. <template>
  2. <div>
  3. <div class="camera-photo" ref="divGenres" v-show="isPhoto" @click="choiceImg">
  4. <img style="width:300px;height:300px;" src="https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1528077222&di=69a2ffcffd12e35216ab71da7a610abe&src=http://img.zcool.cn/community/01f15555b4df7e6ac725ca50c172a1.png@2o.png"/>
  5. <br>
  6. <span>请选择图片上传</span>
  7. <input type="file" ref="uploadImage" @change="onFileChange" accept="image/*" capture="camera" style="display: none;">
  8. </div>
  9.  
  10. <div class="list-li" v-show="show">
  11. <div style="display: inline-block;">
  12. <a class="list-link" @click='previewImage(imgsrc)'>
  13. <img :src="imgsrc">
  14. </a>
  15. <span class="list-img-close" @click='delImage'></span>
  16. </div>
  17. <div class="add-preview" v-show="isPreview" @click="closePreview">
  18. <img :src="previewImg">
  19. </div>
  20. <button type="button" class="upload-button" @click="upload">图片上传</button>
  21. </div>
  22. </div>
  23. </template>
  24.  
  25. <script>
  26. import Bus from '../bus.js'
  27. import qs from "qs"
  28. export default {
  29. data(){
  30. return{
  31. imgsrc:'',//上传的·图片的地址
  32. show:false,//图片放大预览
  33. previewImg: '',//预览图片的地址
  34. isPreview: false,//是否预览当前图片
  35. isPhoto: true,
  36. uploadFile:null
  37. }
  38. },
  39. methods:{
  40.  
  41. choiceImg(){
  42. let self = this;
  43. if (!window.plus){
  44. self.addPic()//如果不支持plus,就用本地相册上传即可
  45. return;
  46. }
  47.  
  48. let title = "选择照片"
  49. let btns = ['拍照','相册']
  50.  
  51. var func = function(e){
  52. var index = e.index;
  53.  
  54. if(index == 1) self.choiceCamera();
  55. if(index == 2) self.choicePic();
  56. }
  57.  
  58. if(title && btns && btns.length > 0){
  59. var btnArray = [];
  60. for(var i=0; i<btns.length; i++){
  61. btnArray.push({title:btns[i]});
  62. }
  63.  
  64. plus.nativeUI.actionSheet({
  65. title : title,
  66. cancel : '取消',
  67. buttons : btnArray
  68. }, function(e){
  69. if(func) func(e);
  70. });
  71. }
  72. },
  73.  
  74. choiceCamera(){
  75. let self = this;
  76. var cmr = plus.camera.getCamera();
  77. cmr.captureImage(function (path){
  78.  
  79. plus.io.resolveLocalFileSystemURL(path, function(entry){
  80. self.imgsrc= entry.toLocalURL();
  81. self.show = true;
  82.  
  83. }, function(e){plus.nativeUI.toast("读取拍照文件错误:" + e.message); });
  84. }, function(e){},{index:1,filename:"_doc/camera/"});
  85. } ,
  86.  
  87. choicePic(){
  88. let self = this;
  89. plus.gallery.pick( function(p){
  90. plus.io.resolveLocalFileSystemURL(p, function(entry) {
  91. self.imgsrc= entry.toLocalURL();
  92. self.show = true;
  93. }, function(e) {
  94. plus.nativeUI.toast("读取拍照文件错误:" + e.message);
  95. });
  96. }, function ( e ) { plus.nativeUI.toast("读取拍照文件错误:" + e.message);}, {
  97. filename: "_doc/camera/",
  98. filter:"image"
  99. } );
  100. },
  101.  
  102. upload(){
  103. var self = this
  104. var wt ;
  105. if (window.plus)
  106. wt = plus.nativeUI.showWaiting();
  107.  
  108. var img = new Image,
  109. width = 512, //image resize 压缩后的宽
  110. quality = 0.5, //image quality 压缩质量
  111. canvas = document.createElement("canvas"),
  112. drawer = canvas.getContext("2d");
  113. img.src = self.imgsrc;
  114. img.onload = function(){//利用canvas压缩图片
  115. canvas.width = width;
  116. canvas.height = width * (img.height / img.width);
  117. drawer.drawImage(img, 0, 0, canvas.width, canvas.height);
  118. var base64 = canvas.toDataURL("image/*", quality);
  119. var pic = base64.split(',')[1];//图片的base64编码内容
  120. var f=self.imgsrc;
  121. var filename=f.replace(f.substring(0, f.lastIndexOf('/') + 1), '');//图片名称
  122.  
  123. if(self.uploadFile !== null){//addPic方法得到的图片文件
  124. filename = self.uploadFile.name
  125. let reader = new FileReader();
  126. reader.readAsDataURL(self.uploadFile);
  127. reader.onload = function(e){
  128. img.src = e.target.result;
  129. }
  130. img.onload = function(){
  131. canvas.width = width;
  132. canvas.height = width * (img.height / img.width);
  133. drawer.drawImage(img, 0, 0, canvas.width, canvas.height);
  134. base64 = canvas.toDataURL("image/*", quality);
  135. pic = base64.split(',')[1];
  136. }
  137. }
          //此处是将图片上传到服务器的代码,略过
  138. }
  139. },
  140.  
  141. onFileChange(e){
  142. let self = this;
  143. let files = e.target.files || e.dataTransfer.files;
  144. if (!files.length) return;
  145. let file = files[0];//File对象
  146. self.uploadFile = file;
  147. let reader = new FileReader();//FileReader对象
  148. reader.readAsDataURL(file);//该方法会读取指定的 Blob 或 File 对象。读取操作完成的时候,readyState 会变成已完成(DONE),并触发 loadend 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。
  149.  
  150. reader.onload = function(e){
  151. self.imgsrc= e.target.result;//图片内容的base64编码
  152. self.show = true;
  153. }
  154. },
  155.  
  156. addPic: function (e) {
  157. let els = this.$refs.divGenres.querySelectorAll('input[type=file]')
  158. els[0].click()
  159. return false
  160. },
  161.  
  162. delImage: function () {
  163. this.imgsrc = "";
  164. this.show = false;
  165. this.isPreview = false;
  166. },
  167.  
  168. previewImage: function (url) {
  169. let vm = this;
  170. vm.isPreview = true;
  171. vm.previewImg = url;
  172. },
  173.  
  174. closePreview: function () {
  175. let vm = this;
  176. vm.isPreview = false;
  177. vm.previewImg = "";
  178. },
  179. },
  180. }
  181. </script>
  182.  
  183. <style>
  184. .upload-button{
  185. display: block;
  186. margin-top: 10px;
  187. }
  188. .camera-photo{
  189. text-align:center;
  190. margin-top:80px;
  191. }
  192. .list-li {
  193.  
  194. display: flex;
  195. flex-direction: column;
  196. align-items: center;
  197. padding: 8px;
  198. margin-top:10px;
  199. position: relative;
  200. text-align: center;
  201. margin-left: auto;
  202. margin-right: auto;
  203. top: 0;
  204. left: 0;
  205. right: 0;
  206. bottom: 0;
  207. }
  208. .list-link img {
  209. width: 150px;
  210. height: 150px;
  211. }
  212. .list-link a:visited {
  213. background-color: #465c71;
  214. border: 1px #4e667d solid;
  215. color: #dde4ec;
  216. display: flex;
  217. line-height: 1.35em;
  218. padding: 4px 20px;
  219. text-decoration: none;
  220. white-space: nowrap;
  221. overflow: hidden;
  222. }
  223. .list-link a:hover {
  224. background-color: #bfcbd6;
  225. color: #465c71;/
  226. text-decoration: none;
  227. }
  228. .list-link a:active {
  229. background-color: #465c71;
  230. color: #cfdbe6;
  231. text-decoration: none;
  232. }
  233. .list-img-close {
  234. background: #ffffff url(https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1526905315674&di=4c2d6a6985b34e141f37bc9fae7f2209&imgtype=0&src=http%3A%2F%2Fpic.58pic.com%2F58pic%2F15%2F55%2F73%2F39I58PICCqK_1024.png) no-repeat right top;
  235. border-color: #ff4a00;
  236. background-position: center;
  237. background-size: 35px 35px;
  238. display: block;
  239. float: left;
  240. top: 5px;
  241. width: 10px;
  242. height: 10px;
  243. position: absolute;
  244. margin-top: 0px;
  245. margin-left: 135px;
  246. padding: 8px;
  247. z-index: 10;
  248. border-radius: 5px;
  249. text-align: center;
  250. }
  251. .add-preview{
  252. width: 300px;
  253. height: 300px;
  254. }
  255. .add-preview img{
  256. width: 100%;
  257. height: 100%;
  258. }
  259. </style>

总之,尝试之后觉得,web工程师如果要做移动端开发,还是得有安卓或者ios工程师的支持。

补充:2018-8-24

  canvas的图片压缩原理,canvas可以改变图片大小,也可以改变图片质量。quality改变图片质量。base64只是对图片对应的二进制码,按照六位对应一个字符规则做转换,转码后是反而比原图片文件大的。但是对于小图片而言,经转换后多出来的字节传输远比多建立一个http连接开销小,所以会利用base64对小图转码来提高页面加载速度。至于图片压缩原理,简单来说,通过算法减少一张图片上的颜色差异,牺牲图片画质。比如紧挨着的颜色相近的四个像素的颜色信息压缩前大概占16个字节,压缩后变成一个颜色就能减少近4倍。quality用来控制色差的力度,值越小力度越大,颜色相差较大的两个像素也会被处理,自然被压缩后文件就越小,画质就越烂。

用base64编码,源文件会变大,base64不能压缩图片,base64就是为了减少http请求。

webAPP如何实现移动端拍照上传(Vue组件示例)?的更多相关文章

  1. 附件上传vue组件封装(一)

    //父页面部分 <attachment @newFileList="newFileList" :operationType="operationType" ...

  2. iOS拍照上传后,在web端显示旋转 Swift+OC版解决方案

    问题描述: 手机头像上传,遇到一个怪现象,就是拍照上传时,手机端显示头像正常,但在web端查看会有一个左旋90度的问题. 并且照片竖怕才会有此问题,横拍不存在. 原因分析: 手机拍照时,用相机拍摄出来 ...

  3. Android4.4 + WebAPI 实现拍照上传

    网上有很多关于拍照上传的实现方法,如果用新版本android去运行有可能会发现根本实现不了.主要原因是android从4.4版本开始通过intent.ACTION_GET_CONTENT打开选择器后, ...

  4. php实现手机拍照上传头像功能

    现在手机拍照很火,那么如何使用手机拍照并上传头像呢?原因很简单,就是数据传递,首先手机传递照片信息,这个就不是post传递 也不是get函数传递, 这个另外一种数据格式传递,使用的是$GLOBALS ...

  5. php实现视频拍照上传头像功能实例代码

    如果要在php中实现视频拍照我们需要借助于flash插件了,由flash拍出的确照片我们再通过php的$GLOBALS ['HTTP_RAW_POST_DATA']接受数据,然后保存成图片就可以了,下 ...

  6. 移动端图片上传解决方案localResizeIMG先压缩后ajax无刷新上传

    现在科技太发达,移动设备像素越来越高,随便一张照片2M+,但是要做移动端图片上传和pc上略有不同,移动端你不能去限制图片大小,让用户先处理图片再上传,这样不现实.所以理解的解决方案就是在上传先进行图片 ...

  7. html5调用手机摄像头,实现拍照上传功能

    今天做手机网站,想实现手机扫描二维码功能.首先实现在浏览器中调用手机摄像头,实现拍照功能并且把拍下的照片显示在页面并上传到服务器上,然后再在服务器端进行分析. 首先实现在浏览器中调用摄像头,当然用现在 ...

  8. 【Demo】HTML5 拍照上传

    本文主要讲解 手机浏览器 如何拍照 为什么会有这个需求 最近做一个项目要用到拍照然后上传照片,但是网页拍照一般都是用Flash做的,而我们主要是H5页面,如果在微信里面有权限就可以通过JSSDK调起摄 ...

  9. Android仿微信图片上传,可以选择多张图片,缩放预览,拍照上传等

    仿照微信,朋友圈分享图片功能 .可以进行图片的多张选择,拍照添加图片,以及进行图片的预览,预览时可以进行缩放,并且可以删除选中状态的图片 .很不错的源码,大家有需要可以下载看看 . 微信 微信 微信 ...

随机推荐

  1. 在过去五分钟内,TypeScript语言服务以外终止了5次

    这个问题困扰了我两次...第一次重装了VS CODE 具体的原意找到之后我直接想骂娘....... 各位如果碰到这个问题....请打开你的360安全卫士!!! 注意看看您家360的防护日志有木有贴心帮 ...

  2. bottle模板中的替换

    line是模板中一行的内容,类似: {{x}}testinfo{{x+10}} x=10时,模板输出: 10testinfo20 x = 10 splits = re.split(r'\{\{(.*? ...

  3. 牛客网小a的旅行计划

    地址:https://ac.nowcoder.com/acm/contest/223/B 此题是一道套公式题目,不难,ans=(pow(4,n+1)-4*pow(3,n+1)+6*pow(2,n+1) ...

  4. vue添加页面键盘事件

    我司开发项目,用的是vue+elementUI,做登陆页面的时候,点击enter键的时候要实现和点击登陆按钮一样的功能,所以就百度了一下,于是一通百度之后,就在点击按钮上面直接添加了@keyup.en ...

  5. 分频器的Verilog实现

    一.占空比50%的奇数分频 1.实现思路 实现奇数(N)分频,分别用上升沿计数到(N-1)/2,再计数到N-1:用下降沿计数到(N-1)/2,再计数到N-1,得到两个波形,然后把它们相或即可得到N分频 ...

  6. python学习第四次笔记

    python学习第四次记录 列表list 列表可以存储不同数据类型,而且可以存储大量数据,python的限制是 536870912 个元素,64位python的限制是 1152921504606846 ...

  7. java编码与解码(一)

    转:https://blog.csdn.net/heyanxi0101/article/details/80356870 java编码与解码(一) 编码表概述和常见的编码表 概述:有字符及其对应的数值 ...

  8. 微信小程序页面跳转,带参数跳转

    1.  wx.navigateTo  (保留当前页面,跳转到应用内的某个页面,使用wx.navigateBack可以返回到原页面.) 路径:只能跳转非tabbar路径 参数:  'path?key=v ...

  9. SQL注入之Sqli-labs系列第五十关,第五十一关,第五十二关,第五十三关(ORDER BY堆叠注入)

    0x1第五十关 源码中使用的mysqli_multi_query()函数,而之前使用的是mysqli_query(),区别在于mysqli_multi_query()可以执行多个sql语句,而mysq ...

  10. 查询MySQL数据库中表结构的几种方法

    什么是表结构?表结构就是定义数据表文件名,确定数据表包含哪些字段,各字段的字段名.字段类型.及宽度,并将这些数据输入到计算机当中. 查询方法:以表‘employees’为例子 1.describe(d ...