npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

svelte-crop-window

v0.1.1

Published

A Svelte component to crop images and videos with touch and mouse gestures

Downloads

126

Readme

svelte-crop-window

A crop window component for images and videos that supports touch gestures (pinch zoom, rotate, pan), as well as mousewheel zoom, mouse-dragging the image, and rotating on right mouse button.

Currently looking for contributors / feature requests / feedback to help improve this component.

video cropper

If you can do code-review, that's very welcome.

Here's a demo page.

Installation

npm install svelte-crop-window

Basic use

You must wrap the CropWindow component with an Element that determines the height.

<script>
    import { CropWindow, defaultValue } from 'svelte-crop-window';

    let media = {
        content_type: 'image',
        url: '/svelte-crop-window/hintersee-3601004.jpg'
    };

    let value = { ...defaultValue };
</script>

<div style="height:20em">
    <CropWindow bind:value {media} />
</div>

CropWindow.svelte Component

Props

| name | type | required | purpose | | --------- | ----------------------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------- | | media | Media | ✓ | image or video to be cropped | | value | CropValue | | value that describes how to crop - will be initialized if undefined | | options | Options | | options for the crop window and overlay, see below |

type Media = {
    content_type: 'image' | 'video';
    url: string;
}

type CropValue = {
    position: { x: number; y: number };
    aspect: number;
    rotation: number;
    scale: number; }
}

const defaultValue: CropValue = {
    position: { x: 0, y: 0 },
    aspect: 1.0,
    rotation: 0,
    scale: 0
};

Options

| name | type | purpose | | -------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | shape | 'rect' \| 'round' | shape of the crop area (yes, an ellipse will work) | | crop_window_margin | number | Margin of the crop window, in pixels. The crop window will always scale to the maximum possible size in its containing element. | | overlay | a Svelte component | The overlay component which visually highlights the crop area. You can pass your own Svelte component with props options: T, gesture_in_progress: boolean, shape: 'rect' \| 'round' here, or use the included Overlay.svelte. | | overlay_options | T | Options for your overlay component. See below for the options of the included overlay component. |

const defaultOptions: Options<OverlayOptions> = {
    shape: 'rect',
    crop_window_margin: 10,
    overlay: Overlay,
    overlay_options: defaultOverlayOptions
};

Overlay.svelte Component

Options

| name | type | purpose | | ------------------ | --------- | -------------------------------------------------------------------- | | overlay_color | string | the color of the overlay that covers everything except the crop area | | line_color | string | the color of the lines | | show_third_lines | boolean | whether to show third lines or not when a gesture is in progress |

const defaultOverlayOptions: OverlayOptions = {
    overlay_color: 'rgb(11, 11, 11)',
    line_color: 'rgb(167, 167, 167)',
    show_third_lines: true
};

How to Crop

Display in HTML Without Actually Cropping:

<div
    style="
        position:relative; overflow:hidden;
        height:{HEIGHT}px; width:{value.aspect * HEIGHT}px;
        border-radius: {options.shape == 'round' ? '50%' : '0'}"
>
    <video
        style="
    transform: translateX(-50%) translateY(-50%) rotate({value.rotation}deg);
    height: {value.scale * HEIGHT}px;
    margin-left: {HEIGHT * value.aspect / 2 + value.position.x * HEIGHT}px;
    margin-top: {HEIGHT / 2 + value.position.y * HEIGHT}px;
    max-width:none"
        src="{url}"
    />
</div>

Note: You must choose a HEIGHT, because the crop value is normalized against the target height.

Pseudo Code to Crop

  1. Choose a target_height and calculate the target_width for the cropped image:
let target_width = value.aspect * target_height;
  1. Calculate factor s by which to scale:
let s = (value.scale * target_height) / media.height;
  1. Scale media by s:
let resized_media = scale(media, s);
  1. Rotate media by value.rotation:
let resized_and_rotated_media = rotate(resized_media, value.rotation);
  1. Calculate top left position of the area to extract:
let left = (resized_and_rotated_media.width - target_width) / 2.0 
            - value.x * target_height;
let top = (resized_and_rotated_media.height - target_height) / 2.0
           - value.y * target_height;
  1. Extract area:
let cropped_media =
    extract_area(resized_and_rotated_media,
                 left, top, target_width, target_height);

What this component doesn't do

  1. Does not modify/crop the image, you have to do that by whatever means make sense for your application. Doesn't (yet) provide usable controls. Currently, you need to implement your own.
  2. Similar to the overlay, it would be nice to include some controls to make this more usable out of the box. Contributions are very welcome.

Developing

Once you've cloned the project and installed dependencies with npm install (or pnpm install or yarn), start a development server:

npm run dev

# or start the server and open the app in a new browser tab
npm run dev -- --open

Acknowledgements

One big inspiration for this component was the Android library uCrop by Yalantis. What is particularly valuable is that the developers shared their thought process in this blog post.

Another very helpful resource was svelte-easy-crop which gave me a basic understanding of how to implement a crop window component in Svelte (and HTML/JS in general).

There's no code reuse between either of these components and this one. All calculations had to be recreated from textbook math.