caractéristique:
- Prend en charge la mise à l'échelle des images de vignettes, en définissant le rapport de mise à l'échelle initial des images de vignettes et la mise à l'échelle en fonction de la position de la souris.
- Prise en charge du chargement par glisser-déposer local
Code source de sgTileImage
<template>
<div :class="$options.name">
<div class="sg-ctrl">
<label>缩放百分比</label>
<el-input-number style="width: 150px;" v-model.trim="scaleValue" :precision="0" :step="10" :min="10" :max="100"
:controls-position="`left`" @change="changeScaleValue" />
</div>
<div class="sg-tile-img" ref="scrollContainer">
<div ref="dragContainer" :style="{ width: `${tileSize * colCount}px` }">
<img v-for="(a, i) in tiles" :key="i" :ref="`tile${i}`" :loaded="a.loaded" :width="tileSize"
:height="tileSize" draggable="false">
</div>
</div>
<sgDragMoveTile :data="dragMoveTileData" @dragMove="dragMove" />
</div>
</template>
<script>
import sgDragMoveTile from "@/vue/components/admin/sgDragMoveTile";
export default {
name: 'sgTileImage',
components: {
sgDragMoveTile
},
data() {
return {
dragMoveTileData: {},
scaleValue: 100,
orginTileSize: 0,
tileSize: 0,
colCount: 0,
rowCount: 0,
tiles: [],//瓦片图数组
mousePoint_bk: null,
}
},
props: [
"data",
/* data格式:{
orginTileSize:500,//瓦片图原始尺寸
colCount: 20,//行数
rowCount: 20,//列数
scaleValue: 100,//缩放百分比
tiles: [],//瓦片图数组
} */
],
watch: {
data: {
handler(newValue, oldValue) {
if (newValue && Object.keys(newValue).length) {
newValue.orginTileSize && (this.orginTileSize = newValue.orginTileSize);
newValue.colCount && (this.colCount = newValue.colCount);
newValue.rowCount && (this.rowCount = newValue.rowCount);
newValue.scaleValue && (this.scaleValue = newValue.scaleValue);
newValue.tiles && (this.tiles = newValue.tiles);
this.$nextTick(() => { this.loadScreenViewTiles(); });
}
},
deep: true,//深度监听
immediate: true,//立即执行
},
scaleValue: {
handler(newValue, oldValue) {
this.tileSize = this.orginTileSize * newValue / 100;
this.$nextTick(() => { this.loadScreenViewTiles(); });
},
deep: true,//深度监听
immediate: true,//立即执行
},
},
destroyed() {
removeEventListener('mousewheel', this.mousewheel);
},
mounted() {
this.dragMoveTileData = {
scrollContainer: this.$refs.scrollContainer,
dragContainer: this.$refs.dragContainer,
}
let rect_scrollContainer = this.$refs.scrollContainer.getBoundingClientRect();
this.mousePoint_bk = {
x: rect_scrollContainer.width / 2,
y: rect_scrollContainer.height / 2,
};
setTimeout(() => {
this.loadScreenViewTiles();
addEventListener('mousewheel', this.mousewheel, { passive: false });
}, 1000);
},
methods: {
// 校正放大缩小后,瓦片图的坐标(目的是为了让缩放看起来是以鼠标坐标为中心点)
centerPosition(e, {
rect_dragContainer_orign,
scrollLeft_orgin,
scrollTop_orgin,
mousePoint }) {
let scrollContainer = this.$refs.scrollContainer;
let dragContainer = this.$refs.dragContainer;
let rect_dragContainer = dragContainer.getBoundingClientRect();
let scale = rect_dragContainer.width / rect_dragContainer_orign.width;//缩放比例
let mouse_left_dis_orgin = scrollLeft_orgin + mousePoint.x;//缩放前鼠标距离瓦片图最左侧的距离
let mouse_top_dis_orgin = scrollTop_orgin + mousePoint.y;//缩放前鼠标距离瓦片图最顶部的距离
let mouse_left_dis = mouse_left_dis_orgin * scale;//缩放后鼠标距离瓦片图最左侧的距离
let mouse_top_dis = mouse_top_dis_orgin * scale;//缩放后鼠标距离瓦片图最顶部的距离
let scrollLeft = mouse_left_dis - mousePoint.x;
let scrollTop = mouse_top_dis - mousePoint.y;
scrollContainer.scrollLeft = scrollLeft;
scrollContainer.scrollTop = scrollTop;
this.mousePoint_bk = mousePoint;
},
mousewheel(e) {
// 记录缩放前的数据
let rect_dragContainer_orign = this.$refs.dragContainer.getBoundingClientRect();
let scrollContainer = this.$refs.scrollContainer;
let mousePoint = { x: e.x, y: e.y };
let scrollLeft_orgin = scrollContainer.scrollLeft;
let scrollTop_orgin = scrollContainer.scrollTop;
// 开始缩放
e.deltaY < 0 && (this.scaleValue += 10);
e.deltaY > 0 && (this.scaleValue -= 10);
// 开始计算坐标
this.$nextTick(() => {
this.centerPosition(e, {
rect_dragContainer_orign,
scrollLeft_orgin,
scrollTop_orgin,
mousePoint
})
});
e.preventDefault && e.preventDefault();//阻止默认的滚轮事件
return false;
},
// 获取浏览器可视范围的瓦片图,并加载图片
loadScreenViewTiles(d) {
let scrollContainer = this.$refs.scrollContainer;
if (scrollContainer) {
let rect_scrollContainer = scrollContainer.getBoundingClientRect();
this.tiles.forEach((v, i) => {
let tile = this.$refs[`tile${i}`];
if (tile) {
tile = tile[0];
let rectTile = tile.getBoundingClientRect();
if (
rectTile.x + rectTile.width > rect_scrollContainer.x - rectTile.width &&
rectTile.y + rectTile.height > rect_scrollContainer.y - rectTile.height &&
rectTile.x < rect_scrollContainer.x + rect_scrollContainer.width &&
rectTile.y < rect_scrollContainer.y + rect_scrollContainer.height
) {
tile.onload = d => { v.loaded = true; };
tile.src = v.img;
}
}
});
}
},
dragMove(d) {
this.loadScreenViewTiles();
},
changeScaleValue(d) {
this.mousewheel(this.mousePoint_bk);
},
},
};
</script>
<style lang="scss" scoped> .sgTileImage {
.sg-ctrl {
position: absolute;
left: 10px;
top: 10px;
z-index: 1;
box-sizing: border-box;
padding: 10px 20px;
background-color: white;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: flex-end;
label {
margin-right: 10px;
}
}
.sg-tile-img {
position: absolute;
left: 0;
top: 0;
overflow: auto;
width: 100%;
height: 100%;
div {
display: flex;
flex-wrap: wrap;
img {
border: none;
opacity: 0;
transition: opacity .382s;
&[loaded] {
opacity: 1;
}
}
}
}
}
</style>
Exemple
<template>
<div>
<sgTileImage :data="tileImageData" />
</div>
</template>
<script>
import sgTileImage from "@/vue/components/admin/sgTileImage";
export default {
components: { sgTileImage },
data() {
return {
tileImageData: {
orginTileSize: 500,//瓦片图原始尺寸
colCount: 20,//行数
rowCount: 20,//列数
tiles: [],//瓦片图数组
}
}
},
created() {
this.tileImageData.tiles = [...Array(this.tileImageData.colCount * this.tileImageData.rowCount)].map((v, i) => ({
loaded: false,
img: `http://shuzhiqiang.com/tiles/${i}.jpg`,
}));//瓦片图数组
},
};
</script>