diff --git a/pages/add/MediaInput.vue b/pages/add/MediaInput.vue index c444a823..74d4c9a4 100644 --- a/pages/add/MediaInput.vue +++ b/pages/add/MediaInput.vue @@ -4,13 +4,19 @@ v-card v-card-title {{$t('common.media')}} v-card-text - v-row - v-col(:span='4' :cols='4') + v-row.mt-1 + v-col#focalPointSelector( + @mousedown='handleStart' @touchstart='handleStart' + @mousemove='handleMove' @touchmove='handleMove' + @mouseup='handleStop' @touchend='handleStop' + ) + div.focalPoint(:style="{ top, left }") + img(v-if='mediaPreview' :src='mediaPreview') + + v-col.col-12.col-sm-4 p {{$t('event.choose_focal_point')}} - v-img(v-if='mediaPreview' - :src='mediaPreview' - aspect-ratio='1.7778' - :position="position") + img.img.d-none.d-sm-block(v-if='mediaPreview' + :src='mediaPreview' :style="{ 'object-position': position }") v-textarea.mt-4(type='text' label='Alternative text' @@ -23,18 +29,13 @@ v-btn(text @click='openMediaDetails=false' color='warning') Cancel v-btn(text color='primary' @click='save') Save - v-col(:span='8' :cols='8') - v-img.cursorPointer( - v-if='mediaPreview' :src='mediaPreview' - @click='selectFocal') - h3.mb-3.font-weight-regular(v-if='mediaPreview') {{$t('common.media')}} v-card-actions(v-if='mediaPreview') v-spacer v-btn(text color='primary' @click='openMediaDetails = true') {{$t('common.edit')}} v-btn(text color='error' @click='remove') {{$t('common.remove')}} div(v-if='mediaPreview') - v-img.col-12.col-sm-2.ml-3(:src='mediaPreview' aspect-ratio='1.7778' :position='savedPosition') + img.img.col-12.ml-3(:src='mediaPreview' :style="{ 'object-position': savedPosition }") span.float-right {{event.media[0].name}} v-file-input( v-else @@ -57,7 +58,8 @@ export default { return { openMediaDetails: false, name: this.value.name || '', - focalpoint: this.value.focalpoint || [0, 0] + focalpoint: this.value.focalpoint || [0, 0], + dragging: false } }, computed: { @@ -68,6 +70,12 @@ export default { const url = this.value.image ? URL.createObjectURL(this.value.image) : /^https?:\/\//.test(this.value.url) ? this.value.url : `/media/thumb/${this.value.url}` return url }, + top () { + return ((this.focalpoint[1] + 1) * 50) + '%' + }, + left () { + return ((this.focalpoint[0] + 1) * 50) + '%' + }, savedPosition () { const focalpoint = this.value.focalpoint || [0, 0] return `${(focalpoint[0] + 1) * 50}% ${(focalpoint[1] + 1) * 50}%` @@ -90,22 +98,39 @@ export default { selectMedia (v) { this.$emit('input', { image: v, name: v.name, focalpoint: [0, 0] }) }, - selectFocal (ev) { - const boundingClientRect = ev.target.getBoundingClientRect() + handleStart (ev) { + ev.preventDefault() + this.dragging = true + this.handleMove(ev, true) + return false + }, + handleStop (ev) { + this.dragging = false + }, + handleMove (ev, manual = false) { + if (!this.dragging && !manual) return + ev.stopPropagation() + const boundingClientRect = document.getElementById('focalPointSelector').getBoundingClientRect() + + const clientX = ev.changedTouches ? ev.changedTouches[0].clientX : ev.clientX + const clientY = ev.changedTouches ? ev.changedTouches[0].clientY : ev.clientY // get relative coordinate - let x = Math.ceil(ev.clientX - boundingClientRect.left) - let y = Math.ceil(ev.clientY - boundingClientRect.top) + let x = Math.ceil(clientX - boundingClientRect.left) + let y = Math.ceil(clientY - boundingClientRect.top) // snap to border - x = x < 20 ? 0 : x > boundingClientRect.width - 20 ? boundingClientRect.width : x - y = y < 20 ? 0 : y > boundingClientRect.height - 20 ? boundingClientRect.height : y + x = x < 30 ? 0 : x > boundingClientRect.width - 30 ? boundingClientRect.width : x + y = y < 30 ? 0 : y > boundingClientRect.height - 30 ? boundingClientRect.height : y + + // this.relativeFocalpoint = [x + 'px', y + 'px'] // map to real image coordinate const posY = -1 + (y / boundingClientRect.height) * 2 const posX = -1 + (x / boundingClientRect.width) * 2 this.focalpoint = [posX, posY] + return false } } } @@ -115,4 +140,36 @@ export default { cursor: crosshair; } +.img { + width: 100%; + object-fit: cover; + object-position: top; + aspect-ratio: 1.7778; +} + +#focalPointSelector { + position: relative; + cursor: move; + overflow: hidden; + display: flex; + height: 100%; + justify-self: center; + align-items: center; +} + +#focalPointSelector img { + width: 100%; +} + +.focalPoint { + position: absolute; + width: 50px; + height: 50px; + top: 100px; + left: 100px; + transform: translate(-25px, -25px); + border-radius: 50%; + border: 1px solid #ff6d408e; + box-shadow: 0 0 0 9999em rgba(0, 0, 0, .65); +}