| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- <template>
- <div class="pic-img">
- <div class="img-container" @mousemove="!moveEvent && mouseMove($event)" @mouseleave="!leaveEvent && mouseLeave($event)">
- <img ref="img" :src="!lazyload ? url : imgLoadedFlag && url" style="width:100%" @load="!lazyload && imgLoaded($event)" />
- <div
- v-if="!hideZoom && imgLoadedFlag && !hideSelector"
- :class="['img-selector', { circle: type === 'circle' }]"
- :style="[imgSelectorStyle, imgSelectorSize, imgSelectorPosition, !outShow && imgBg, !outShow && imgBgSize, !outShow && imgBgPosition]"
- >
- <slot></slot>
- </div>
- <div
- v-if="outShow"
- v-show="!hideOutShow"
- :class="['img-out-show', { 'base-line': baseline }]"
- :style="[imgOutShowSize, imgOutShowPosition, imgBg, imgBgSize, imgBgPosition]"
- >
- <div v-if="pointer" class="img-selector-point"></div>
- </div>
- </div>
- </div>
- </template>
- <script>
- export default {
- props: {
- url: {
- type: String,
- default: ''
- },
- highUrl: {
- type: String,
- default: ''
- },
- width: {
- type: Number,
- default: 168
- },
- type: {
- type: String,
- default: 'circle',
- validator: function(value) {
- return ['circle', 'square'].indexOf(value) !== -1;
- }
- },
- selectorStyle: {
- type: Object,
- default() {
- return {};
- }
- },
- outShowStyle: {},
- scale: {
- type: Number,
- default: 3
- },
- lazyload: {
- type: Boolean,
- default: false
- },
- moveEvent: {
- type: [Object, MouseEvent],
- default: null
- },
- leaveEvent: {
- type: [Object, MouseEvent],
- default: null
- },
- hideZoom: {
- type: Boolean,
- default: false
- },
- outShow: {
- type: Boolean,
- default: false
- },
- pointer: {
- type: Boolean,
- default: false
- },
- baseline: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- selector: {
- width: this.width,
- top: 0,
- left: 0,
- bgTop: 0,
- bgLeft: 0,
- rightBound: 0,
- bottomBound: 0,
- absoluteLeft: 0,
- absoluteTop: 0
- },
- imgInfo: {},
- $img: null,
- screenWidth: document.body.clientWidth,
- outShowInitTop: 0,
- outShowTop: 0,
- hideOutShow: true,
- imgLoadedFlag: false,
- highImgLoadedFlag: false,
- hideSelector: true,
- timer: null
- };
- },
- computed: {
- addWidth() {
- return !this.outShow ? (this.width / 2) * (1 - this.scale) : 0;
- },
- imgSelectorPosition() {
- const { top, left } = this.selector;
- return {
- top: `${top}px`,
- left: `${left}px`
- };
- },
- imgSelectorSize() {
- const width = this.selector.width;
- return {
- width: `${width}px`,
- height: `${width}px`
- };
- },
- imgSelectorStyle() {
- return this.selectorStyle;
- },
- imgOutShowSize() {
- const {
- scale,
- selector: { width }
- } = this;
- return {
- width: `${width * scale}px`,
- height: `${width * scale}px`
- };
- },
- imgOutShowPosition() {
- return {
- top: `${this.outShowTop}px`,
- right: `${-8}px`
- };
- },
- imgBg() {
- return {
- backgroundImage: `url(${this.highUrl || this.url})`
- };
- },
- imgBgSize() {
- const {
- scale,
- imgInfo: { height, width }
- } = this;
- return {
- backgroundSize: `${width * scale}px ${height * scale}px`
- };
- },
- imgBgPosition() {
- const { bgLeft, bgTop } = this.selector;
- return {
- backgroundPosition: `${bgLeft}px ${bgTop}px`
- };
- }
- },
- watch: {
- moveEvent(e) {
- this.mouseMove(e);
- },
- leaveEvent(e) {
- this.mouseLeave(e);
- },
- url() {
- this.handlerUrlChange();
- },
- width(n) {
- this.initSelectorProperty(n);
- },
- screenWidth(val) {
- if (!this.timer) {
- this.screenWidth = val;
- this.timer = setTimeout(() => {
- this.imgLoaded();
- clearTimeout(this.timer);
- this.timer = null;
- }, 400);
- }
- }
- },
- created() {
- this.url && this.lazyload && this.handlerUrlChange();
- },
- mounted() {
- this.$img = this.$refs['img'];
- },
- methods: {
- handlerUrlChange() {
- this.imgLoadedFlag = false;
- this.lazyload && this.loadImg(this.url).then(this.imgLoaded, err => console.error(err));
- },
- loadImg(url) {
- return new Promise((resolve, reject) => {
- const img = document.createElement('img');
- img.addEventListener('load', resolve);
- img.addEventListener('error', reject);
- img.src = url;
- });
- },
- imgLoaded() {
- const imgInfo = this.$img.getBoundingClientRect();
- if (JSON.stringify(this.imgInfo) != JSON.stringify(imgInfo)) {
- this.imgInfo = imgInfo;
- this.initSelectorProperty(this.width);
- this.resetOutShowInitPosition();
- }
- if (!this.imgLoadedFlag) {
- this.imgLoadedFlag = true;
- this.$emit('created', imgInfo);
- }
- },
- mouseMove(e) {
- if (!this.hideZoom && this.imgLoadedFlag) {
- this.imgLoaded();
- const { pageX, pageY, clientY } = e;
- const { scale, selector, outShow, addWidth, outShowAutoScroll } = this;
- let { outShowInitTop } = this;
- const scrollTop = pageY - clientY;
- const { absoluteLeft, absoluteTop, rightBound, bottomBound } = selector;
- const x = pageX - absoluteLeft; // 选择器的x坐标 相对于图片
- const y = pageY - absoluteTop; // 选择器的y坐标
- if (outShow) {
- if (!outShowInitTop) {
- outShowInitTop = this.outShowInitTop = scrollTop + this.imgInfo.top;
- }
- this.hideOutShow && (this.hideOutShow = false);
- this.outShowTop = scrollTop > outShowInitTop ? scrollTop - outShowInitTop : 0;
- }
- this.hideSelector && (this.hideSelector = false);
- selector.top = y > 0 ? (y < bottomBound ? y : bottomBound) : 0;
- selector.left = x > 0 ? (x < rightBound ? x : rightBound) : 0;
- selector.bgLeft = addWidth - x * scale; // 选择器图片的坐标位置
- selector.bgTop = addWidth - y * scale;
- }
- },
- initSelectorProperty(selectorWidth) {
- const selectorHalfWidth = selectorWidth / 2;
- const selector = this.selector;
- const { width, height, left, top } = this.imgInfo;
- const scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
- const scrollLeft = document.documentElement.scrollLeft || window.pageXOffset || document.body.scrollLeft;
- selector.width = selectorWidth;
- selector.rightBound = width - selectorWidth;
- selector.bottomBound = height - selectorWidth;
- selector.absoluteLeft = left + selectorHalfWidth + scrollLeft;
- selector.absoluteTop = top + selectorHalfWidth + scrollTop;
- },
- mouseLeave() {
- this.hideSelector = true;
- if (this.outShow) {
- this.hideOutShow = true;
- }
- },
- reset() {
- Object.assign(this.selector, {
- top: 0,
- left: 0,
- bgLeft: 0,
- bgTop: 0
- });
- this.resetOutShowInitPosition();
- },
- resetOutShowInitPosition() {
- this.outShowInitTop = 0;
- }
- }
- };
- </script>
- <style scoped>
- .img-container {
- position: relative;
- }
- .img-selector {
- position: absolute;
- cursor: crosshair;
- border: 1px solid rgba(0, 0, 0, 0.1);
- background-repeat: no-repeat;
- background-color: rgba(0, 0, 0, 0.6);
- }
- .img-selector.circle {
- border-radius: 50%;
- }
- .img-out-show {
- position: absolute;
- background-repeat: no-repeat;
- transform: translate(100%, 0);
- border: 1px solid rgba(0, 0, 0, 0.1);
- }
- .img-selector-point {
- position: absolute;
- width: 4px;
- height: 4px;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background-color: black;
- }
- .img-out-show.base-line::after {
- position: absolute;
- box-sizing: border-box;
- content: '';
- width: 1px;
- border: 1px dashed rgba(0, 0, 0, 0.36);
- top: 0;
- bottom: 0;
- left: 50%;
- transform: translateX(-50%);
- }
- .img-out-show.base-line::before {
- position: absolute;
- box-sizing: border-box;
- content: '';
- height: 1px;
- border: 1px dashed rgba(0, 0, 0, 0.36);
- left: 0;
- right: 0;
- top: 50%;
- transform: translateY(-50%);
- }
- </style>
|