效果:
点击从手机相机获取图片或打开相机拍摄图片, 触屏缩放移动 加载的图片到合适位置
注意这里的几个坑:
1 在红米K40上 出现了 微信浏览器点击,触发change事件后只能调到相册,无法弹出“相机或相册选择框”,而且无法选中相片的问题。
参考 前段H5开发,小米上用input File 为什么获取不到照片的数据? - 知乎
解决方法如下代码。
<!-- 写成 accept="image/png,image/jpeg,image/gif" 时 红米k40 微信浏览器无法打开相册 要写成 accept="image/*" -->
<!-- <input type="file" id="image_uploads" multiple="" class="inputFile" accept="image/png,image/jpeg,image/gif" > -->
<!-- <input type="file" id="image_uploads" multiple="" class="inputFile" accept="image/*" > -->
只不过解决后因为问题2 又都注释掉了。
2 在ios系统上 出现了 第一次点击,经常无法触发change事件的情况
具体现象:安卓手机上,微信浏览器、自带浏览器都没有问题。苹果手机上,第一次点击,有时候无法触发change事件(复现频率较高),第二次点击就好了。访问过一次,点击,好了后,刷新页面也不一定能复现问题。(坑死)
参考 https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only
解决方法:
将initNew() 方法里下面这两行注释掉
// this.input = document.querySelector('#image_uploads')
// this.input.addEventListener('change', this.updateImageDisplay)
将 template 里的 <input> 元素注释掉
<!-- 写成 accept="image/png,image/jpeg,image/gif" 时 红米k40 微信浏览器无法打开相册 要写成 accept="image/*" -->
<!-- <input type="file" id="image_uploads" multiple="" class="inputFile" accept="image/png,image/jpeg,image/gif" > -->
<!-- <input type="file" id="image_uploads" multiple="" class="inputFile" accept="image/*" > -->
换成initNew() 方法里
// input 元素绑定 change 事件
let mobileCamera = document.querySelector('#mobileCamera')
let input = document.createElement("input");
input = document.body.appendChild(input)
input.type = "file"
input.id = "image_uploads"
input.className="inputFile";
input.multiple= true
input.accept="image/png,image/jpeg,image/gif"
input.style.width= "1%"
input.style.height = "1%"
input.style.position = 'absolute';
input.style.opacity = 0;
input.addEventListener('change', this.updateImageDisplay)
注意这里
input = document.body.appendChild(input) ,绑定在 document.body 上,一开始绑定在 <div id="mobileCamera" class="mobileCamera" v-loading="loading"> 上,未解决问题,至于绑定在其他dom上可以不,还未测试。
绑定change事件用 ,input.addEventListener('change', this.updateImageDisplay)
用onchange 未生效。
3 功能设计里,是用img 或者div点击后,去触发 input 元素的click事件的。而这种情况下,在ios系统上 出现了 img、div 需要双击的情况才能触发click,input 支持单击。 Android手机无此问题。
参考 tap事件_苏醒!的博客-CSDN博客_tap事件
解决方法
let mobileCamera = document.querySelector('#mobileCamera')
// 用 tab 换掉 click ,click在ios safari浏览器上有 单击失效,需要双击 的问题
this.bindTapEvent(mobileCamera,(e)=>{
input.click();
})
bindTapEvent(dom,callback){
var startTime = 0;
var isMove = false;
dom.addEventListener('touchstart',function(){
startTime = Date.now();
console.log(startTime);
});
dom.addEventListener('touchmove',function(){
isMove = true;
});
dom.addEventListener('touchend',function(e){
console.log(Date.now()-startTime);
if((Date.now()-startTime)<250&&isMove == false){
callback&&callback.call(this,e)
}else{
console.log('失败');
}
isMove = false;
startTime = 0;
});
},
直接上代码
<template>
<div class="component">
<div id="mobileCamera" class="mobileCamera" v-loading="loading">
<!-- 写成 accept="image/png,image/jpeg,image/gif" 时 红米k40 微信浏览器无法打开相册 要写成 accept="image/*" -->
<!-- <input type="file" id="image_uploads" multiple="" class="inputFile" accept="image/png,image/jpeg,image/gif" > -->
<!-- <input type="file" id="image_uploads" multiple="" class="inputFile" accept="image/*" > -->
<div class="imgPreviewDiv" v-show="src">
<img id="mobileCameraPreview" :src="src" class="imgPreview" >
</div>
<img src="./assets/b.png" class="imgCamera" v-if="show">
</div>
</div>
</template>
import Hammer from 'hammerjs'
import axios from 'axios'
export default {
name: 'conponent01',
props: {
},
computed: {},
data () {
return {
input: undefined,
src: "",
show1:false,
show:true,
loading:false,
// 拖动缩放
imgScale: 1,
oldScale: 1,
imgPos: {
x: 0,
y: 0
},
}
},
mounted () {
// 图片操作 移动 缩放
this.zoom()
this.initNew()
},
methods: {
// 同事写的 移动端 移动、缩放元素
zoom(){
var square = this.$el.querySelector('#mobileCameraPreview')
let that = this
// Create an instance of Hammer with the reference.
var hammer = new Hammer(square)
hammer.get('pinch').set({ enable: true })
// Subscribe to a quick start event: press, tap, or doubletap.
// For a full list of quick start events, read the documentation.
hammer.on('panend', function (e) {
that.imgPos.x += e.deltaX
that.imgPos.y += e.deltaY
})
hammer.on('pan', function (e) {
// console.log('pan=====', e)
square.style.transform = `matrix(${that.imgScale},0,0,${that.imgScale},${that.imgPos.x + e.deltaX},${that.imgPos.y + e.deltaY})`
})
hammer.on('pinch', function (e) {
that.imgScale = Math.max(Math.min(that.oldScale * e.scale, 10), 1)
square.style.transform = `matrix(${that.imgScale},0,0,${that.imgScale},${that.imgPos.x},${that.imgPos.y})`
})
hammer.on('pinchend', function (e) {
that.oldScale = that.imgScale
})
},
initNew () {
// input 元素绑定 change 事件
let mobileCamera = document.querySelector('#mobileCamera')
let input = document.createElement("input");
input = document.body.appendChild(input)
input.type = "file"
input.id = "image_uploads"
input.className="inputFile";
input.multiple= true
input.accept="image/png,image/jpeg,image/gif"
input.style.width= "1%"
input.style.height = "1%"
input.style.position = 'absolute';
input.style.opacity = 0;
input.addEventListener('change', this.updateImageDisplay)
// 在ios系统上 出现了 第一次点击,经常无法触发change事件的情况
// 参考 https://stackoverflow.com/questions/47664777/javascript-file-input-onchange-not-working-ios-safari-only
// 将下面这两行 和 template 里的 <input> 元素注释掉 换成上面这个 createElement('input')
// this.input = document.querySelector('#image_uploads')
// this.input.addEventListener('change', this.updateImageDisplay)
// 用 tab 换掉 click ,click在ios safari浏览器上有 单击失效,需要双击 的问题
this.bindTapEvent(mobileCamera,(e)=>{
input.click();
})
},
// 参考 https://blog.csdn.net/qq_42309685/article/details/102526869
bindTapEvent(dom,callback){
var startTime = 0;
var isMove = false;
dom.addEventListener('touchstart',function(){
startTime = Date.now();
console.log(startTime);
});
dom.addEventListener('touchmove',function(){
isMove = true;
});
dom.addEventListener('touchend',function(e){
console.log(Date.now()-startTime);
if((Date.now()-startTime)<250&&isMove == false){
callback&&callback.call(this,e)
}else{
console.log('失败');
}
isMove = false;
startTime = 0;
});
},
imgClick () {
this.input.click();
},
updateImageDisplay () {
let _this = this
this.loading = true
const curFiles = this.input.files
if (curFiles.length === 0) {
// no file
} else {
for (const file of curFiles) {
// 方法1 上传后台 返回服务器端图片地址 赋值给 src
var formData = new FormData()
formData.append('files',file)
axios({
method: 'post',
url: 'https://xxxxxx/api/ossupload/uploadFile',
data: formData
}).then((res)=>{
console.log(res.data.data[0].path)
_this.show1 = true
_this.show = false
_this.$nextTick(()=>{
_this.src = res.data.data[0].path
_this.loading = false
// 图片操作相关
_this.imgScale = 1
_this.oldScale = 1
_this.imgPos = {
x: 0,
y: 0
}
_this.$el.querySelector('#mobileCameraPreview').style.transform = `matrix(1,0,0,1,0,0)`
})
})
// 方法2 读成 base64 赋值给 src
// var reader = new FileReader()
// reader.readAsDataURL(file)
// reader.onload = function () {
// _this.show1 = true
// _this.show = false
// _this.$nextTick(()=>{
// _this.src = this.result
// _this.imgScale = 1
// _this.oldScale = 1
// _this.imgPos = {
// x: 0,
// y: 0
// }
// _this.$el.querySelector('#mobileCameraPreview').style.transform = `matrix(1,0,0,1,0,0)`
// })
// }
}
}
},
},
}
</script>
<style scoped>
.component {
width: 100%;
height: 100%;
}
.mobileCamera{
width: 100%;
height: 100%;
cursor: pointer;
border-radius: 50%;
display:flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.imgPreviewDiv {
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: 0 0 15px 10px #d5d1a775;
overflow: hidden;
display:flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.imgPreview {
width: 100%;
box-shadow: 0 0 15px 10px #d5d1a775;
}
.imgCamera {
width: 80px;
height: 80px;
}
.inputFile {
width: 1%;
height: 1%;
position:absolute;
opacity: 0;
}
</style>