I am struggling to get cropperjs to work in svelte. Any help would be appreciated.
The svelte script is as follows
<script>
import Cropper from 'cropperjs';
const image = document.getElementById('image');
const cropper = new Cropper(image, {
aspectRatio: 16 / 9,
crop(event) {
console.log(event.detail.x);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
</script>
<img id="image" src="./images/favicon.png" alt="">
The error is:
Uncaught Error: The first argument is required and must be an <img> or <canvas> element.
at new Cropper (cropper.js:3229:15)
at instance$1 (Example5.svelte:8:14)
at init (index.mjs:1809:11)
at new Example5 (Example5.svelte:17:36)
at create_fragment (Example5.svelte:17:36)
at init (index.mjs:1824:37)
at new App (Example5.svelte:17:36)
at main.js:3:13
at main.js:7:2
There appears to be an issue using import Cropper from 'cropperjs'. Corrl supplied a work-around in a comment:
There seems to be a problem with
import Cropper from 'cropperjs';inside the REPL - when using<svelte:head>with script and stylesheet references to Cropper it looks better
Another problem is that the image hasn't completed loading by the time you pass the reference to Cropper. You could try using onMount instead which runs after the component is added to the dom. However, the image data hasn't always completed loading when onMount runs.
Another option is to bind to the onload event for the image. This answer to Svelte component onLoad shows the technique. However, you could simply call img.addEventListener('load', initCropper); within onMount which will initialize Cropper once the image has fully loaded.
<svelte:head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/2.0.0-alpha.2/cropper.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/2.0.0-alpha.2/cropper.min.js"></script>
</svelte:head>
<script>
import { onMount } from 'svelte'
let img, cropper;
onMount(() => {
// call initCropper when img is loaded
img.addEventListener('load', initCropper);
});
function initCropper() {
cropper = new Cropper(img, {
aspectRatio: 16 / 9,
viewMode: 2,
crop(event) {
console.log(event.detail);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
}
</script>
<div>
<img {src} bind:this={img} />
</div>
The code above works for me, you can see a demo here.
Alternative
There is a cropping library - svelte-easy-crop - which works with Svelte. The code below is taken from the demo:
<script>
import Cropper from "svelte-easy-crop";
let crop = { x: 0, y: 0 };
let zoom = 1;
let image = "https://cdn1-www.dogtime.com/assets/uploads/2011/03/puppy-development.jpg";
</script>
<Cropper {image} bind:crop bind:zoom />
yes the suggestion by Corrl was spot on. Adding the head elements sorted out the problem. Also the point by haldo, about using onMount is required as well.
An alternative to initializing Cropper inside onMount would be use:action > Docs > REPL
<svelte:head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/2.0.0-alpha.2/cropper.css" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/2.0.0-alpha.2/cropper.min.js"></script>
</svelte:head>
<script>
let cropper;
function initCropper(node){
console.log('loaded', node.complete)
cropper = new Cropper(node, {
aspectRatio: 16 / 9,
crop(event) {
console.log(event.detail);
console.log(event.detail.y);
console.log(event.detail.width);
console.log(event.detail.height);
console.log(event.detail.rotate);
console.log(event.detail.scaleX);
console.log(event.detail.scaleY);
},
});
}
</script>
<div>
<img use:initCropper src="https://cdn1-www.dogtime.com/assets/uploads/2011/03/puppy-development.jpg" alt="">
</div>
<style>
img {
display: block;
max-width: 100%;
}
</style>