@mlbrgn/image-editor
v1.0.126
Published
An image editor web component / custom element
Readme
Image Editor
The ImageEditor is a custom web component, built with JavaScript, that allows users to perform various edits on images, such as cropping, resizing, rotating, and applying filters.
Development
This package is developed inside the mlbrgn-node-workspace monorepo.
Do not run npm run dev or npm install inside this repository.
Clone the monorepo instead: https://github.com/evertjanmlbrgn/mlbrgn-node-workspace
Installation and Import
To use the ImageEditor, import it as a "side effect import":
import "path/to/ImageEditor.js"This class extends ImageEditorCustomElement and does not export anything directly, but registers the custom element.
local / dev test page
to visit the local dev / test page run:
npm run devand click the link shown at "Local", e.g. http://localhost:xxxx/ the x's should be replaced with the port number.
Key Features
The ImageEditor provides the following functionalities:
- Image Loading and Display: Loads images from various sources and displays them within the editor.
- Cropping/Selection: Allows users to select a specific area of the image for cropping, with support for aspect ratio locking and snapping.
- Resizing: Enables resizing of images to specific dimensions.
- Rotation: Supports image rotation.
- Flipping and Flopping: Provides options to horizontally or vertically flip the image.
- Image Filtering: Applies various filters to the image.
- Dynamic UI Updates: Reacts to user interactions and updates the UI in real-time.
- File Size and Format Information: Displays current and target image file sizes and allows conversion to different formats.
- Responsive Design: Adjusts handle sizes for touch devices.
Core Concepts
Custom Element
The ImageEditor is a web component, meaning it's a reusable HTML element (<image-editor>) that encapsulates its functionality and styling. It extends ImageEditorCustomElement, providing a base for its custom element behavior.
Shadow DOM
The editor uses Shadow DOM (static shadowTemplate = mainTemplate) to encapsulate its internal structure and styles, preventing them from interfering with the rest of the document.
Canvases
The editor utilizes two main HTML canvases:
canvasImage: Used for drawing and manipulating the actual image.canvasDraw: Used for drawing selection boxes, handles, grid lines, and other interactive elements on top of the image.
Image Dimensions and Scaling
The editor carefully manages various dimensions and scaling factors to ensure accurate drawing and display across different resolutions and zoom levels:
editorWidth,editorHeight: Dimensions of the container holding the canvases.imageNaturalWidth,imageNaturalHeight: Original dimensions of the loaded image.canvasImageWidth,canvasImageHeight: Calculated dimensions forcanvasImageto fit within boundaries, considering rotation.canvasDrawWidth,canvasDrawHeight: Dimensions forcanvasDraw, scaled for sharp drawing on potentially low-resolution images.canvasesCSSScaleRatio: Ratio by which thecanvasesWrapperneeds to be scaled for CSS display.canvasDrawRatio: Scaling factor betweencanvasImageandcanvasDrawfor drawing elements correctly.zoomRatio: User-controlled zoom level.
Selection and Handles
The image editor provides robust selection functionality:
selectionArea: Represents the currently selected area of the image in original image coordinates.selectionAreaScaled: The selected area scaled to thecanvasDrawdimensions for accurate drawing of the selection box and handles.selectionHandleAreas: A collection ofAreaobjects representing the interactive handles (corners and edges) for resizing the selection. Each handle has atype(corner/edge),mode(grab/resize), andaction(e.g., 'nw-resize').selectionMode: Determines the current interaction mode (e.g., 'select', 'resize', 'grab').selectionAction: Specifies the active resizing or grabbing action (e.g., 'nw-resize', 'grab').selectionAnimationController: Manages the animation of the selection box dashed lines.
Event Handling
The ImageEditor uses an EventBus for communication and handling various events, such as status messages (onCanvasStatusMessage). It also utilizes ResizeObserver to react to changes in the editor's container size.
Public Methods
constructor(): Initializes the custom element, creates canvases, disables controls, sets up filters, determines file format support, and adds event listeners.getImageFileConfiguration(): Returns an object defining image file constraints (e.g.,formatsRegex,minWidth,maxWidth,maxUploadFileSize).connectedCallback(): Lifecycle method called when the custom element is added to the DOM.disconnectedCallback(): Lifecycle method called when the custom element is removed from the DOM.enableControls(enable): Enables or disables the editor's control elements (menu and canvas buttons).updateFilter(canvasImageFilter): Applies a CSS filter string to the image canvas and redraws the image.
Private Methods
#createCanvases(): Creates and appends thecanvasImageandcanvasDrawelements to the DOM.#initializeResizeObserver(): Sets up aResizeObserverto redraw the canvas when the editor's container resizes.#calculateFileSize(mimeType): Calculates the size of the image on the canvas in the specified MIME type.#checkConversionSupport(mimeType): Checks if a given MIME type is supported for image conversion by the browser.#formatBytes(bytes, decimals = 2): Formats a number of bytes into a human-readable string (e.g., "1.2 MB").#getImageAspectRatioLabel(width, height): Calculates and formats the aspect ratio of an image as a fraction, including common aspect ratio labels.#calculateCanvasImageSize(imageWidth, imageHeight, rotationAngle): Calculates the required canvas size to fit a rotated image without cropping.#updateImageDimensions(): Recalculates all relevant image and canvas dimensions based on current state (rotation, original image size, editor size).#calculateCanvasesCssDimensions(): Determines the CSS dimensions for the canvases wrapper to fit within the editor's boundaries.#calculateCssScaleRatio(): Calculates the CSS scale factor needed to fit the image within the editor's display area.#getCorrectedDimension(value): Corrects a dimension value for zooming, CSS scaling, andcanvasDrawRatio.#getCorrectedDimension2(value): Corrects a dimension value for zooming and CSS scaling, but notcanvasDrawRatio.#dispatchStatusMessage(message): Fires anonCanvasStatusMessageevent with the given message.#applyImageDimensions(): Applies the calculated width and height to the canvas elements' attributes and CSS styles.#draw(): Main drawing function, which calls#drawImage()and#drawDrawing().#drawImage(): Draws the current image ontocanvasImage, applying rotation, flipping, flopping, and filters.#resizeImage(resizeWidth, resizeHeight): Resizes the current image to the specified dimensions and updates the editor.#updateSelectionHandles(): Calculates and updates the positions and sizes of the selection handles based on theselectionArea.#calculateGridLines(): Calculates the positions of grid and sub-grid lines.#drawDrawing(): Draws the selection box, handles, and grid lines ontocanvasDraw.#limitAndSnapSelectedArea(selectedArea): Adjusts the selected area to snap to canvas edges if within a defined threshold.#checkOutOfBounds(area): Checks if a given area is outside the bounds of the image canvas.#updateSelectedArea(selectedArea): Updates theselectionAreaand its scaled version, and then updates selection properties.#validateSelectedArea(selectedArea): Checks if theselectedAreameets the minimum and maximum width/height constraints.#drawSelectionBox(): Draws the dashed selection box oncanvasDraw.#startSelectionAnimation(): Starts the animation for the selection box dashed lines.animateSelection(): Updates theanimationOffsetfor the selection box animation.#stopSelectionAnimation(): Pauses the selection box animation.#drawGrabCross(): Draws a crosshair in the center of the selection area (not currently used in the provided code).#drawSelectionHandles(): Draws the interactive handles around the selection area.#drawGrid(): Draws the grid and sub-grid lines oncanvasDraw.#getPointerRelativeToCanvasStart(clientX, clientY): Converts client-side pointer coordinates to coordinates relative to the canvas origin, considering scaling.#updatePointerStart(clientX, clientY): Updates the starting pointer position for selection/resizing.#updatePointer(clientX, clientY): Updates the current pointer position.#getPointerOverArea(): Determines which selection handle (if any) the pointer is currently over and updates the cursor.#getAspectRatioFromCurrentSelection(): Returns the aspect ratio of the current selection.#getAspectRatioValueFromSelect(): Retrieves the selected aspect ratio value from the aspect ratio dropdown.#drawAnchorPoint(anchorPoint): Draws a small rectangle at a given anchor point (used for debugging/visualization).#applySelectBoxRatioToWidth(area): Adjusts the width of an area to maintain a specific aspect ratio.#applySelectBoxRatioToHeight(area): Adjusts the height of an area to maintain a specific aspect ratio.#determineSelectionMode(): Determines whether the user is in 'select', 'resize', or 'grab' mode based on pointer position.#selectStart(): Initiates a new selection, clearing any previous selection.#selectMove(shiftKey = false): Handles pointer movement during selection, adjusting the selected area and redrawing.#resizeStart(): Prepares for a resizing operation by storing the initial selection and determining the anchor point.
Properties
Static Properties
shadowTemplate: Reference to the HTML template for the Shadow DOM.elementLookup: An array of CSS selectors used to find and store references to key DOM elements within the component.translationsPath: The path to the translation files.observedAttributes: An array of attribute names that the component should observe for changes (currently onlydisabled).
Instance Properties
#overRideAspectRatio: Internal property to temporarily override the aspect ratio for selection (e.g., when Shift key is pressed).#fileSizeOriginal: Stores the original file size of the loaded image.canvasFilters: An instance ofCanvasFiltersfor managing image filters.#originalImage: Stores the originalImageobject.#originalImageFile: Stores the originalImageFileobject.#currentImage: Stores the currently displayedImageobject.#currentImageId: ID of the current image.#currentImageFile: Stores theImageFileobject for the currently displayed image.#initiallySelected: Stores the selection area at the start of a resize operation.#initiallySelectedHalfway: Stores the midpoint of the selection area at the start of a resize operation.selectionAnimationController: Controls the animation of the selection box.editorWidth,editorHeight: Width and height of theeditorelement.imageNaturalWidth,imageNaturalHeight: Natural (unscaled) width and height of the current image.canvasImageWidth,canvasImageHeight: Width and height ofcanvasImage.canvasDrawRatio: Scaling ratio betweencanvasImageandcanvasDraw.canvasDrawWidth,canvasDrawHeight: Width and height ofcanvasDraw.canvasesCSSScaleRatio: CSS scaling factor for the canvases.canvasesCSSWidth,canvasesCSSHeight: CSS width and height of the canvases.canvasImageXOrigin,canvasImageYOrigin: Center point ofcanvasImage.canvasImageDrawStart: Starting point for drawing the image oncanvasImage.flipXAxisDirection,flipYAxisDirection: Direction multipliers for horizontal and vertical flipping.flipXOrigin,flipYOrigin: Origin points for flipping transformations.imageAspectRatio: Calculated aspect ratio of the current image.imageOrientation: Orientation of the current image (Landscape, Portrait, Square).gridGap: Spacing between major grid lines.lineWidth: Line width for drawing on canvases.selectionLineDashSize: Dash size for the selection box line.crossLineSize: Size of the crosshair lines.handleCornerSize,handleEdgeSize,handleEdgeMargin: Dimensions for selection handles.zoomPercentage: Current zoom level as a percentage.canvasImageFilter: Current CSS filter string applied to the image.showGrid: Boolean indicating whether the grid should be displayed.selectionArea: AnAreaobject representing the current selection.selectionAreaScaled: A scaled version ofselectionAreafor drawing.selectionHandleAreas: An object containingAreaobjects for each selection handle.selectionValid: Boolean indicating if the current selection meets size constraints.animationOffset: Offset for the selection box dash animation.gridLines: Array of objects defining grid line start and end points.selectionPointerStart:Pointobject storing the starting pointer position for an interaction.selectionPointerCurrent:Pointobject storing the current pointer position.selectionStartPointerOver: Information about which handle the pointer was over when an interaction started.selectionWasTouchEvent: Boolean indicating if the last selection interaction was a touch event.
Configuration (this.config)
The ImageEditor relies on a config object (likely inherited from ImageEditorCustomElement) for various settings, including:
animateFPS: Frames per second for selection animation.aspectRatioTolerance: Tolerance for matching aspect ratios.minWidth,maxWidth,minHeight,maxHeight: Minimum and maximum dimensions for image editing.maxUploadFileSize,maxEditFileSize: Maximum file sizes for upload and editing.drawCanvasWidth: Target width for thecanvasDrawelement.gridLineCount: Number of major grid lines.lineWidth: Default line width for drawing.selectionLineDashSize: Default dash size for selection box.crossLineSize: Default size for crosshair lines.handleCornerSize,handleEdgeSize,handleEdgeMargin: Default sizes for selection handles.touchHandleMultiplierBreakpoint: Breakpoint for adjusting handle sizes on touch devices.touchHandleMultiplier: Multiplier for handle sizes on touch devices.selectionBoxStrokeStyle: CSS stroke style for the selection box.selectionBoxLineDashStrokeStyle: CSS stroke style for the dashed selection box.selectionBoxInvalidLineDashStrokeStyle: CSS stroke style for invalid selection box.crossStrokeStyle: CSS stroke style for the crosshair.selectionHandleStrokeStyle: CSS stroke style for selection handles.selectionHandleOverFillStyle: CSS fill style for hovered selection handles.selectionHandleLineDashStrokeStyle: CSS stroke style for dashed selection handles.gridStrokeStyle: CSS stroke style for major grid lines.showSubGrid: Boolean to enable or disable sub-grid display.subGridStrokeStyle: CSS stroke style for sub-grid lines.snapThresholdPercentage: Percentage threshold for snapping selection to canvas edges.animateSelection: Boolean to enable or disable selection animation.
Usage Example (Conceptual)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Image Editor</title>
<style>
image-editor {
display: block;
width: 800px;
height: 600px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<image-editor id="myImageEditor"></image-editor>
<script type="module">
import "./path/to/ImageEditor.js"; // Import as side effect
const imageEditor = document.getElementById('myImageEditor');
// Example: Load an image
// This functionality would typically be exposed via methods on the ImageEditorCustomElement
// or through external controls interacting with the custom element.
// For demonstration, imagine a function to load an image:
// imageEditor.loadImage('/path/to/your/image.jpg');
// Example: Listen for status messages
document.addEventListener('onCanvasStatusMessage', (event) => {
console.log('Status:', event.detail.message);
});
// You would interact with the image editor through its exposed properties and methods
// or through events dispatched by the custom element.
</script>
</body>
</html>
## Events
The ImageEditor custom element dispatches the following events that can be listened to from outside the custom element:
### canvasStatusMessage
Fired when there's a status message from the canvas.
```javascript
imageEditor.addEventListener('canvasStatusMessage', function(event) {
console.log('Canvas status message:', event.detail.message);
});closeImageEditor
Fired when the image editor is closed.
imageEditor.addEventListener('closeImageEditor', function(event) {
console.log('Image editor closed');
});imageSave
Fired when an image is saved.
imageEditor.addEventListener('imageSave', function(event) {
console.log('Image saved:', event.detail.id, event.detail.file);
});Usage Example
document.addEventListener("DOMContentLoaded", function () {
let imageEditor = document.querySelector('#image-editor');
// Listen to events from the custom element
imageEditor.addEventListener('canvasStatusMessage', function(event) {
console.log('Canvas status message:', event.detail.message);
});
imageEditor.addEventListener('closeImageEditor', function(event) {
console.log('Image editor closed');
});
imageEditor.addEventListener('imageSave', function(event) {
console.log('Image saved:', event.detail.id, event.detail.file);
});
imageEditor.setImage('heart', 'test/images/heart.png', 'heart');
}, false);
---