mirror of
https://github.com/ipfs/ipfs-blog.git
synced 2026-06-22 02:55:23 +02:00
feat: add image crop
This commit is contained in:
committed by
João Peixoto
parent
aa5641b2e4
commit
aa79148a13
Generated
+25
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -140,7 +140,6 @@ module.exports = {
|
||||
{
|
||||
normalSuffix: '/',
|
||||
indexSuffix: '/',
|
||||
notFoundPath: '/404/',
|
||||
},
|
||||
],
|
||||
[
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 |
@@ -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 |
@@ -1,12 +0,0 @@
|
||||
---
|
||||
sidebar: auto
|
||||
# sidebarDepth: 2
|
||||
---
|
||||
|
||||
# Hello from test
|
||||
|
||||
## Level 2
|
||||
|
||||
## Level 3
|
||||
|
||||
## Level 4
|
||||
Reference in New Issue
Block a user