feat: add image crop

This commit is contained in:
João Peixoto
2021-02-16 14:36:50 +00:00
committed by João Peixoto
parent aa5641b2e4
commit aa79148a13
12 changed files with 224 additions and 24 deletions
+25
View File
@@ -4850,6 +4850,11 @@
}
}
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@@ -6782,6 +6787,11 @@
"integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=",
"dev": true
},
"debounce": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.0.tgz",
"integrity": "sha512-mYtLl1xfZLi1m4RtQYlZgJUNQjl4ZxVnHzIR8nLLgi4q1YT8o/WM+MK/f8yfcc9s5Ir5zRaPZyZU6xs1Syoocg=="
},
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
@@ -7430,6 +7440,11 @@
}
}
},
"easy-bem": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/easy-bem/-/easy-bem-1.0.2.tgz",
"integrity": "sha512-tHtLDhcEHZIMKdiiZElQoR8TcZ/6rvcNp7//93Vx/mqNLah9BOFGhhzTUfWLJs7uxZiKMdP/KzGOtzq14DrrqQ=="
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
@@ -18311,6 +18326,16 @@
"integrity": "sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg==",
"dev": true
},
"vue-advanced-cropper": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/vue-advanced-cropper/-/vue-advanced-cropper-1.3.0.tgz",
"integrity": "sha512-KALBnQVfiIr98uNqhbpJq5yOGxANOuTC0wsJx9SCvw6k1MAp8Nv8b4Qi8rJ8AXqS5OQ4451XDyLsr0jemYjWkw==",
"requires": {
"classnames": "^2.2.6",
"debounce": "^1.2.0",
"easy-bem": "^1.0.2"
}
},
"vue-class-component": {
"version": "7.2.6",
"resolved": "https://registry.npmjs.org/vue-class-component/-/vue-class-component-7.2.6.tgz",
+1
View File
@@ -73,6 +73,7 @@
"tailwindcss-typography": "^3.1.0",
"v-lazy-image": "^1.4.0",
"v-scroll-lock": "^1.3.1",
"vue-advanced-cropper": "^1.3.0",
"vue-mq": "^1.0.1",
"vue-multiselect": "^2.1.6",
"vue-observe-visibility": "^0.4.6",
-1
View File
@@ -140,7 +140,6 @@ module.exports = {
{
normalSuffix: '/',
indexSuffix: '/',
notFoundPath: '/404/',
},
],
[
+3
View File
@@ -6,10 +6,13 @@ import VueMq from 'vue-mq'
import { VLazyImagePlugin } from 'v-lazy-image'
import VueObserveVisibility from 'vue-observe-visibility'
import VueSocialSharing from 'vue-social-sharing'
import ImageCrop from '@theme/layouts/ImageCrop'
import Transition from '@theme/components/directives/Transition.js'
export default ({ Vue, router, siteData }) => {
router.addRoutes([{ path: '/image-crop', component: ImageCrop }])
const { breakpoints } = siteData.themeConfig
/**
-11
View File
@@ -44,17 +44,6 @@
Load More
</button>
</div>
<!-- <div
v-if="infiniteScroll && pagesToShow.length < publicPages.length"
v-observe-visibility="{
callback: handleBottomVisibilityChange,
intersection: {
rootMargin: '-360px 0px -360px 0px',
threshold: 1.0,
},
}"
class="teste"
></div> -->
</div>
<VideoModal ref="videoModal" />
</Layout>
+191
View File
@@ -0,0 +1,191 @@
<template>
<div class="flex justify-center items-center h-screen grid-margins">
<div v-if="croppedImage" class="flex flex-col">
<div>
<img :src="croppedImage" />
</div>
<div class="flex items-center justify-center mt-4">
<button
class="bg-blueGreen bg-opacity-75 transition duration-300 hover:bg-opacity-100 text-white px-4 py-2 mr-4"
@click="download"
>
Download
</button>
<button
class="bg-blueGreen bg-opacity-75 transition duration-300 hover:bg-opacity-100 text-white px-4 py-2"
@click="reset"
>
Load a new image
</button>
</div>
</div>
<div v-if="image && !croppedImage" class="relative w-full cropper">
<cropper
ref="cropper"
class="cropper"
:debounce="false"
:src="image"
:stencil-props="{
aspectRatio: 1.91,
}"
/>
<div class="flex flex-col absolute buttons">
<button
class="bg-blueGreen bg-opacity-75 hover:bg-opacity-100 transition duration-300 ease-in-out text-white p-2 mb-4"
@click="zoom(1.5)"
>
<SVGIcon
class="color-white fill-current w-6 h-6"
name="zoom-in"
title="Zoom in"
/>
</button>
<button
class="bg-blueGreen bg-opacity-75 hover:bg-opacity-100 transition duration-300 ease-in-out text-white p-2 mb-4"
@click="zoom(0.5)"
>
<SVGIcon
class="color-white fill-current w-6 h-6"
name="zoom-out"
title="Zoom out"
/>
</button>
<button
class="bg-blueGreen bg-opacity-75 hover:bg-opacity-100 transition duration-300 ease-in-out text-white p-2 mb-4"
@click="crop"
>
<SVGIcon
class="color-white fill-current w-6 h-6"
name="save-icon"
title="Save"
/>
</button>
<button
class="bg-blueGreen bg-opacity-75 hover:bg-opacity-100 transition duration-300 ease-in-out text-white p-2"
@click="reset"
>
<SVGIcon
class="color-white fill-current w-6 h-6"
name="trash-icon"
title="Delete"
/>
</button>
</div>
</div>
<div
v-if="!image && !croppedImage"
class="image-upload flex flex-col items-center cursor-pointer justify-center bg-white text-blueGreen text-center h-screen w-full max-w-2xl opacity-75 hover:opacity-100 transition-all duration-300 ease-in-out border-2 border-dashed rounded text-lg"
@dragover="dragover"
@dragleave="dragleave"
@drop="drop"
@click="$refs.file.click()"
>
<input
ref="file"
class="hidden"
type="file"
accept="image/*"
@change="loadImage($event)"
/>
Drag and drop an image<br />
(or click to browse)
</div>
</div>
</template>
<script>
import { Cropper } from 'vue-advanced-cropper'
import SVGIcon from '@theme/components/base/SVGIcon.vue'
import 'vue-advanced-cropper/dist/style.css'
export default {
name: 'ImageCrop',
components: {
Cropper,
SVGIcon,
},
data() {
return {
image: null,
imageName: '',
croppedImage: null,
}
},
methods: {
zoom(factor) {
this.$refs.cropper.zoom(factor)
},
reset() {
this.image = null
this.croppedImage = null
},
dragover(event) {
event.preventDefault()
if (!event.currentTarget.classList.contains('border-solid')) {
event.currentTarget.classList.remove('border-dashed')
event.currentTarget.classList.add('border-solid')
}
},
dragleave(event) {
event.currentTarget.classList.add('border-dashed')
event.currentTarget.classList.remove('border-solid')
},
drop(event) {
event.preventDefault()
this.loadImage({ target: { files: event.dataTransfer.files } })
event.currentTarget.classList.add('border-dashed')
event.currentTarget.classList.remove('border-solid')
},
loadImage(event) {
const input = event.target
if (input.files && input.files[0]) {
const reader = new FileReader()
reader.onload = (e) => {
this.image = e.target.result
this.imageName = input.files[0].name
}
reader.readAsDataURL(input.files[0])
}
},
crop() {
const { coordinates, canvas } = this.$refs.cropper.getResult()
this.coordinates = coordinates
this.croppedImage = canvas.toDataURL()
},
download() {
let fileName = this.imageName.split('.')
const extension = fileName.pop()
fileName = fileName.join('.')
const currentDate = new Date()
const year = currentDate.getFullYear()
const month = ('0' + (currentDate.getMonth() + 1)).slice(-2)
const day = ('0' + currentDate.getDate()).slice(-2)
const newFile = `${year}-${month}-${day}-cardheader-${fileName}.${extension}`
const a = document.createElement('a')
a.href = this.croppedImage
a.download = newFile
a.click()
},
},
}
</script>
<style scoped>
.image-upload {
max-height: 30rem;
border-color: #3e9096;
}
.cropper {
max-height: 70vh;
}
.buttons {
top: 50%;
left: 2rem;
transform: translateY(-50%);
}
</style>
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13 3h2.996v5h-2.996v-5zm11 1v20h-24v-24h20l4 4zm-17 5h10v-7h-10v7zm15-4.171l-2.828-2.829h-.172v9h-14v-9h-3v20h20v-17.171z"/></svg>

After

Width:  |  Height:  |  Size: 223 B

@@ -0,0 +1 @@
<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M19 24h-14c-1.104 0-2-.896-2-2v-17h-1v-2h6v-1.5c0-.827.673-1.5 1.5-1.5h5c.825 0 1.5.671 1.5 1.5v1.5h6v2h-1v17c0 1.104-.896 2-2 2zm0-19h-14v16.5c0 .276.224.5.5.5h13c.276 0 .5-.224.5-.5v-16.5zm-9 4c0-.552-.448-1-1-1s-1 .448-1 1v9c0 .552.448 1 1 1s1-.448 1-1v-9zm6 0c0-.552-.448-1-1-1s-1 .448-1 1v9c0 .552.448 1 1 1s1-.448 1-1v-9zm-2-7h-4v1h4v-1z"/></svg>

After

Width:  |  Height:  |  Size: 464 B

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13 10h-3v3h-2v-3h-3v-2h3v-3h2v3h3v2zm8.172 14l-7.387-7.387c-1.388.874-3.024 1.387-4.785 1.387-4.971 0-9-4.029-9-9s4.029-9 9-9 9 4.029 9 9c0 1.761-.514 3.398-1.387 4.785l7.387 7.387-2.828 2.828zm-12.172-8c3.859 0 7-3.14 7-7s-3.141-7-7-7-7 3.14-7 7 3.141 7 7 7z"/></svg>

After

Width:  |  Height:  |  Size: 361 B

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M13 10h-8v-2h8v2zm8.172 14l-7.387-7.387c-1.388.874-3.024 1.387-4.785 1.387-4.971 0-9-4.029-9-9s4.029-9 9-9 9 4.029 9 9c0 1.761-.514 3.398-1.387 4.785l7.387 7.387-2.828 2.828zm-12.172-8c3.859 0 7-3.14 7-7s-3.141-7-7-7-7 3.14-7 7 3.141 7 7 7z"/></svg>

After

Width:  |  Height:  |  Size: 341 B

-12
View File
@@ -1,12 +0,0 @@
---
sidebar: auto
# sidebarDepth: 2
---
# Hello from test
## Level 2
## Level 3
## Level 4