创建一个组件插入body中,使用固定定位生成一个全局遮罩层,再把图片放进去
创建组件/components/PreviewImage/index.vue
搭建组件结构,传入url数组,以及打开的当前图片索引下标,以便进行翻页查看
如果遮罩层后面页面有滚动条时在组件打开时,需要禁止背景内容随鼠标滚轮滚动,每次打开关闭时给body动态增加样式 overflow: hidden属性即可
<template>
<div v-if="show" class="previewImage_wrapper">
<div class="previewImage_image">
<img :src="previewImgList[currentIndex] || ''">
</div>
<div class="previewImage_close previewImage_btn" @click="closePreviewImage">×</div>
<div class="previewImage_navigation">
<span class="previewImage_navigation_left previewImage_btn" @click="prevImage"><</span>
<span class="previewImage_navigation_right previewImage_btn" @click="nextImage">></span>
</div>
</div>
</template>
<script>
export default {
props: {
visible: { // 显示控制
type: Boolean,
default: false
},
previewImgList: { // url数组
type: Array,
default: () => []
},
currentIndex: { // 当前图片索引
type: Number,
default: 0
}
},
computed: {
// 双向绑定
show: {
get() {
return this.visible
},
set(newVal) {
this.$emit('update:visible', newVal)
}
}
},
watch: {
visible: { // 给body动态增加style属性,禁止背景内容的鼠标滚轮滚动
handler(newVal) {
if(newVal) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "";
}
}
},
},
methods: {
// 上一张图片
prevImage() {
if (this.currentIndex === 0) {
this.currentIndex = this.previewImgList.length - 1
} else {
this.currentIndex--
}
},
// 下一张图片
nextImage() {
if (this.currentIndex === this.previewImgList.length - 1) {
this.currentIndex = 0
} else {
this.currentIndex++
}
},
// 关闭预览图片组件
closePreviewImage() {
this.show = false
}
},
mounted() { // 插入body
document.body.appendChild(this.$el);
},
destroyed() { // 组件销毁后同步清除元素
this.$el.parentNode.removeChild(this.$el);
}
}
在打开遮罩层时加上过渡效果,让组件体验更好
定义过渡动画,使用vue推荐使用自带的 组件,这样切换显示隐藏都会触发过渡效果,如果以class类名的形式定义的过渡动画,在使用指令时v-if或者v-show 隐藏关闭时不会触发结束的过渡效果
<transition name="zoom">
<!-- 组件 -->
</transition>
.zoom-enter, .zoom-leave-to { // 元素进入和离开时的动作
transform: scale(0);
}
.zoom-enter-active, .zoom-leave-active { // 元素进入和离开时的过渡动画定义
transition: transform 0.3s;
}
效果如下
效果实现了,接下来还可以加入更多的功能
在图片底部加一个控制工具栏,例如对预览图片的控制,放大、缩小、翻转等。在封装组件实现功能的时候我们应当先实现基础功能,再深入开发细节功能
想要实现元素的放大缩小,翻转,可以直接利用css3中transform属性中的 scale rotate,然后使用 js 进行动态控制
<script>
export default {
data() {
return {
imgHandle: { // 图片控制
scale: 1,
rotate: 0
}
}
},
methods: {
// 初始化还原图片缩放旋转控制
async initImgHandle() {
this.imgHandle = {
scale: 1,
rotate: 0
}
await this.$nextTick()
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 放大图片
async largeHandle() {
console.log(this.imgHandle.scale, 'scale')
this.imgHandle.scale = Number((this.imgHandle.scale + 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 缩小图片
async shrinkHandle() {
console.log(this.imgHandle.scale, 'scale')
if (this.imgHandle.scale === 0.2) { // 最低缩放到0.2倍
return
}
this.imgHandle.scale = Number((this.imgHandle.scale - 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
}
}
</script>
在进行小数点计算的时候,要注意小数点的精度问题,不然可能会导致计算出错产生bug,这里使用 toFixed 来解决下
在 JavaScript 中处理小数计算时,会遇到舍入误差导致计算结果不准确的情况。这是由于 JavaScript 中采用的是双精度浮点数格式(IEEE 754 标准)来表示数字,而这种格式无法准确地表示某些十进制小数
接下来写旋转的方法,然后给元素绑定点击事件就ok了
<script>
export default {
methods: {
// 向左翻转
async turnLeftHandle() {
this.imgHandle.rotate = this.imgHandle.rotate - 90
await this.$nextTick()
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 向右翻转
async turnRightHandle() {
this.imgHandle.rotate = this.imgHandle.rotate + 90
await this.$nextTick()
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
}
}
</script>
最后记得给img图片加上过渡效果 transition: transform 0.3s ease; ,当控制图片操作的时候更平滑
最后的效果如下
组件完整代码/components/PreviewImage/index.vue
<template>
<transition name="zoom">
<div v-if="show" class="previewImage_wrapper" @wheel="handleScroll">
<div class="previewImage_image">
<img ref="previewImage_img" :src="previewImgList[currentIndex] || ''">
</div>
<div class="previewImage_close previewImage_btn" @click="closePreviewImage">×</div>
<div class="previewImage_navigation">
<span class="previewImage_navigation_left previewImage_btn" @click="prevImage"><</span>
<span class="previewImage_navigation_right previewImage_btn" @click="nextImage">></span>
</div>
<div class="previewImage_toolbar">
<span class="previewImage_btn" @click="shrinkHandle">-</span>
<span class="previewImage_btn" @click="largeHandle">+</span>
<span class="previewImage_btn" @click="turnLeftHandle">↺</span>
<span class="previewImage_btn" @click="initImgHandle">▣</span>
<span class="previewImage_btn" @click="turnRightHandle">↻</span>
</div>
</div>
</transition>
</template>
<script>
export default {
props: {
visible: { // 显示控制
type: Boolean,
default: false
},
previewImgList: { // url数组
type: Array,
default: () => []
},
currentIndex: { // 当前图片索引
type: Number,
default: 0
}
},
data() {
return {
imgHandle: { // 图片控制
scale: 1,
rotate: 0
}
}
},
computed: {
// 双向绑定
show: {
get() {
return this.visible
},
set(newVal) {
this.$emit('update:visible', newVal)
}
}
},
watch: {
visible: { // 给body动态增加style属性,禁止背景内容的鼠标滚轮滚动
handler(newVal) {
if(newVal) {
document.body.style.overflow = "hidden";
this.initImgHandle() // 每次打开图片初始化
} else {
document.body.style.overflow = "";
}
}
},
},
methods: {
// 鼠标滚轮
handleScroll(event) {
if (event.deltaY > 0) {
// 向下滚动事件
// console.log('向下滚动');
this.shrinkHandle()
} else {
// 向上滚动事件
// console.log('向上滚动');
this.largeHandle()
}
},
// 向左翻转
async turnLeftHandle() {
this.imgHandle.rotate = this.imgHandle.rotate - 90
await this.$nextTick()
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 向右翻转
async turnRightHandle() {
this.imgHandle.rotate = this.imgHandle.rotate + 90
await this.$nextTick()
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 初始化还原图片缩放旋转控制
async initImgHandle() {
this.imgHandle = {
scale: 1,
rotate: 0
}
await this.$nextTick()
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 放大图片
async largeHandle() {
console.log(this.imgHandle.scale, 'scale')
this.imgHandle.scale = Number((this.imgHandle.scale + 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 缩小图片
async shrinkHandle() {
console.log(this.imgHandle.scale, 'scale')
if (this.imgHandle.scale === 0.2) { // 最低缩放到0.2倍
return
}
this.imgHandle.scale = Number((this.imgHandle.scale - 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准
const element = this.$refs.previewImage_img
element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`
},
// 上一张图片
prevImage() {
if (this.currentIndex === 0) {
this.currentIndex = this.previewImgList.length - 1
} else {
this.currentIndex--
}
this.initImgHandle()
},
// 下一张图片
nextImage() {
if (this.currentIndex === this.previewImgList.length - 1) {
this.currentIndex = 0
} else {
this.currentIndex++
}
this.initImgHandle()
},
// 关闭预览图片组件
closePreviewImage() {
this.show = false
}
},
mounted() { // 插入body
document.body.appendChild(this.$el);
},
destroyed() { // 组件销毁后同步清除元素
this.$el.parentNode.removeChild(this.$el);
}
}
</script>
<style lang="less" scoped>
.previewImage_wrapper{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: rgba(0, 0, 0, .5);
z-index: 9999;
.previewImage_image{
display: flex;
align-items: center;
justify-content: center;
img {
width: 100vw;
height: 100vh;
object-fit: scale-down;
transition: transform 0.3s ease;
}
}
.previewImage_close{
position: absolute;
right: 20px;
top: 20px;
transition: transform 0.2s ease-out;
&:hover{
transform: scale(1.2);
}
}
.previewImage_navigation{
&_left{
position: absolute;
left: 15px;
top: 50%;
transform: translate(0, -50%);
transition: transform 0.2s ease-out;
}
&_right{
position: absolute;
right: 15px;
top: 50%;
transform: translate(0, -50%);
transition: transform 0.2s ease-out;
}
&_left:hover,&_right:hover{
transform: translate(0, -50%) scale(1.2);
}
}
.previewImage_toolbar{
position: absolute;
bottom: 10px;
left: 50%;
transform: translate(-50%, 0);
display: flex;
align-items: center;
span{
margin-right: 10px;
transition: transform 0.2s ease-out;
&:hover{
transform: scale(1.1) ;
}
}
span:last-child{
margin-right: 0;
}
}
.previewImage_btn{
width: 50px;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
color: #fff;
background-color: #606266;
border-radius: 50%;
cursor: pointer;
}
}
.zoom-enter, .zoom-leave-to { // 元素进入和离开时的动作
transform: scale(0);
}
.zoom-enter-active, .zoom-leave-active { // 元素进入和离开时的过渡动画定义
transition: transform 0.3s;
}
.slide-enter, .slide-leave-to { // 元素进入和离开时的动作
transform: translateX(100%);
}
.slide-enter-active, .slide-leave-active { // 元素进入和离开时的过渡动画定义
transition: transform 0.3s ease-in-out;
}
</style>
使用组件代码
这样一个需求,就是在一个DIV中包含有一个Image标签,但是在Div标签中包含有一张背景图片,设计图上的样子是这张背景图片是有一个透明度的,但是如果直接使用opacity属性设置的的话就会连Div中的内容的透明度也会受到影响,那么我们如何在HTML中设置div背景图片的透明度呢?,可以通过以下几种方法实现。
这是在日常开发中被推荐使用的方法,通过这种方式实现不会影响到div中的其他内容的透明度只会影响它自己背景的透明度,详细实现如下。
<!DOCTYPE html>
<html>
<head>
<style>
.container {
position: relative;
width: 300px;
height: 200px;
overflow: hidden;
}
.container::before {
content: "";
background-image: url('your-image.jpg');
background-size: cover;
background-position: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0.5; /* 调整透明度 */
z-index: 1;
}
.content {
position: relative;
z-index: 2;
color: white;
}
</style>
</head>
<body>
<div class="container">
<div class="content">
这里是内容
</div>
</div>
</body>
</html>
这种方式比较适合那种需要给背景图片上添加蒙版的情况,但是笔者尝试的时候,结果实在是不尽人意。所以还是选择了上面的推荐方法,不过这种方式要比上面的那种方式实现起来要简单很多。如下所示。
<!DOCTYPE html>
<html>
<head>
<style>
.container {
width: 300px;
height: 200px;
background: rgba(255, 255, 255, 0.5) url('your-image.jpg') no-repeat center center;
background-size: cover;
}
</style>
</head>
<body>
<div class="container">
这里是内容
</div>
</body>
</html>
这种方式实现会影响到整个的div的样式,也就是说页面中的内容的透明度也会受到影响,并且这种影响不会被其他样式所改变。如下所示。
<!DOCTYPE html>
<html>
<head>
<style>
.container {
width: 300px;
height: 200px;
background: url('your-image.jpg') no-repeat center center;
background-size: cover;
filter: opacity(0.5); /* 调整透明度 */
}
</style>
</head>
<body>
<div class="container">
这里是内容
</div>
</body>
</html>
以上就是实现如何调整div的背景透明度,在一些特殊场景中我们还可以通过JS的方式来实现。上面的方法中,推荐使用的是伪元素方法,因为它在修改了div背景透明度之后,并不会影响到其他的元素,RGBA色彩添加则是局限于一些色彩华丽的地方使用,而对于一些单色调的内容来讲这种方式实现效果不是太好。通过CSS过滤样式,虽然是最直接的方式,但是如果在div内部有内容的情况下会影响到整个组件体系的样式。
在实际开发中,我们可以选择合适的方式来实现这个需求。当然还有其他的实现方式,有兴趣的读者可以留言我们一起讨论。
S方法:
$("body").attr('style','overflow-y:hidden') //这个是解决竖状滚动条
//横向 需要把overflow-y改成overflow-x即可
CSS办法:
原始处理方法是将要展示的图片进行处理。比如你的DIV宽度为500px(像素),那你上传的图片或放入网页的图片宽度就要小于500px,也就是你图片需要图片软件剪切、等比例缩小方法处理后再上传、放入网页中解决撑破撑开DIV问题。
常见很多大型图片站点、新闻站点都是将照片图片进行处理适应网页宽度情况下,进行图片编辑处理的。
如果不处理照片方法适应DIV有限宽度,那可以对DIV设置隐藏超出内容方法。只需要对DIV设置宽度后加入CSS样式“overflow:hidden”即可解决隐藏图片比DIV过宽部分解决撑破DIV问题
三、解决方法三
对图片img标签中只加入宽度即可解决。这样可以等比例缩小图片,不会影响图片画面质量。
比如你的网页DIV宽度为500px,那你上传图片后对img标签设置width等于500以下即可。
<img src="图片路径" width="小于你的DIV宽度" />即可解决图片过宽导致DIV SPAN撑破,这样好处可以等比例放大缩小图片
四、CSS解决撑破方法四
这种方法使用CSS直接对div内的img进行宽度设置,这样不好是如果图片过小会影响网页浏览图片时候效果。
Div结构:<div class="dc5"><img src="图片路径" /></div>
对应CSS代码:.dc5 img{宽度值+单位}
五、CSS解决图片撑破撑开DIV方法五
使用max-width(最大宽度),比如你DIV宽度为500px,那你对应DIV样式再加入最大宽度CSS样式“max-width="500px"”即可解决,但是在IE6浏览器不兼容此属性,谨慎使用。
六、解决图片撑破DIV层方法总结与推荐
1)、最大宽度(max-width)+overflow:hidden。我们这样设置可以让IE6版本以上浏览器支持最大宽度样式,也让IE6下隐藏图片超出宽度而撑开DIV得到解决,此方法比较方便和实用。
2)、只使用overflow:hidden属性,如方法二
3)、图片使用上传时候软件处理下,以适应DIV布局宽度,如方法一
以上为推荐解决IMG图片撑破有限DIV宽度方法,根据实际情况大家可以任意选择适合自己解决网页中图片撑破DIV层方法。
*请认真填写需求信息,我们会在24小时内与您取得联系。