aico-image-editor
Version:
Combine multiple image into and create single combined image
314 lines (296 loc) • 25.4 kB
HTML
<div x-data="configurator2d">
<div>
<div class="row g-3">
<div class="col-12 col-xxxl-7 d-flex">
<form class="text-center mb-0" enctype="multipart/form-data" :class="$store.canvas.isSaveButtonDisabled ? '':''">
<dropdown-menu datatemplateurl="./dropdownMenu.html" styles="global" shadow="true" x-data="dropdownMenu"
class="btn-group btn-group-lg" dropdown-trigger-class="dropdown-toggle-split btn-elegance-primary"
data-bs-auto-close="outside" data-bs-strategy="fixed">
<a class="btn btn-lg btn-elegance-primary" type="button" @click="$store.canvas.saveCanvasImage(2000)"
:href="$store.canvas.savedImageDownloadUrl"
slot="dropdown-append">
<span x-text="getTranslatedHTML('saveText')"></span>
</a>
<span slot="dropdown-trigger" class="pe-none">
<span class="visually-hidden">Toggle Dropdown</span>
</span>
<ul slot="dropdown-content" class="list-unstyled" >
<div class="form-check form-switch form-check-inline mb-2">
<input class="form-check-input" type="checkbox" value="" id="show-crop" name="show-crop" x-model="$store.canvas.showCropSelectionRect"
@change="$store.canvas.toggleCropSelectionRect()">
<label class="form-check-label" for="show-crop" x-text="getTranslatedHTML('crop')"></label>
</div>
<div class="mb-2" :class="!$store.canvas.showCropSelectionRect ? 'inactiveblock':''">
<div class="d-flex align-items-center mb-1">
<h6 class="mb-0" x-text="getTranslatedHTML('cropCanvas')"></h6>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="row">
<div class="col-6">
<label class="form-label" for="canvas-width" x-text="getTranslatedHTML('canvasCropWidth')"></label>
<input id="canvas-width" type="number" class="form-control form-control-sm border-primary border-2"
x-model="$store.canvas.canvasCropWidth" @change="$store.canvas.updateCropSelectionRect()" />
</div>
<div class="col-6">
<label class="form-label" for="canvas-height" x-text="getTranslatedHTML('canvasCropHeight')"></label>
<input id="canvas-height" type="number" class="form-control form-control-sm border-primary border-2"
x-model="$store.canvas.canvasCropHeight" @change="$store.canvas.updateCropSelectionRect()" />
</div>
</div>
</div>
<div>
<div class="d-flex align-items-center mb-1">
<h6 class="mb-0" x-text="getTranslatedHTML('imageDimensions')"></h6>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="row gy-2 mb-3">
<div class="col-6">
<label class="form-label" for="final-image-width" x-text="getTranslatedHTML('finalImageWidth')"></label>
<input id="final-image-width" type="number" min="1" :max="$store.canvas.maxCanvasWidth" class="form-control form-control-sm border-primary border-2"
x-model="$store.canvas.finalImageWidth" @input="() => {
$store.canvas.finalImageWidth = Math.min($store.canvas.finalImageWidth,$el.max)
$store.canvas.finalImageWidth = Math.max($store.canvas.finalImageWidth,$el.min)
}" />
</div>
<div class="col-6">
<label class="form-label" for="final-image-height" x-text="getTranslatedHTML('finalImageHeight')"></label>
<input id="final-image-height" type="number" min="1" :max="$store.canvas.maxCanvasHeight" class="form-control form-control-sm border-primary border-2"
x-model="$store.canvas.finalImageHeight" @input="() => {
$store.canvas.finalImageHeight = Math.min($store.canvas.finalImageHeight,$el.max)
$store.canvas.finalImageHeight = Math.max($store.canvas.finalImageHeight,$el.min)
}" />
</div>
</div>
<div class="space-x-2 mb-2">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="scaling-fit" value="fit" x-model="$store.canvas.scalingStretegy" />
<label class="form-check-label" for="scaling-fit" x-text="getTranslatedHTML('fit')"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="scaling-fill" value="fill" x-model="$store.canvas.scalingStretegy">
<label class="form-check-label" for="scaling-fill" x-text="getTranslatedHTML('fill')"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="scaling-stretch" value="stretch" x-model="$store.canvas.scalingStretegy">
<label class="form-check-label" for="scaling-stretch" x-text="getTranslatedHTML('stretch')"></label>
</div>
</div>
<div class="mt-3">
<label class="form-label" for="image-size-presets" x-text="getTranslatedHTML('templateImageSIzesLabel')"></label>
<select class="form-select form-select-sm border" id="image-size-presets" x-model="presetImageSize"
@change="updateFinalImageDim()">
<option value="1080x1920">Instagram Reels - <small>1080 x 1920 px</small></option>
<option value="820x312">Facebook Cover Photo - 820 x 312 px</option>
<option value="1500x500">Twitter Header Image - 1500 x 500 px</option>
<option value="1584x396">LinkedIn Banner Image - 1584 x 396 px</option>
<option value="1280x720">YouTube Thumbnail - 1280 x 720 px</option>
<option value="1000x1500">Pinterest Pin - 1000 x 1500 px</option>
<option value="250x250">Google+ Profile Picture - 250 x 250 px</option>
<option value="1080x1920">Snapchat Story - 1080 x 1920 px</option>
<option value="1080x1920">TikTok Video - 1080 x 1920 px</option>
<option value="1920x1080">Website Hero Image - 1920 x 1080 px</option>
</select>
</div>
<div class="mt-3">
<div class="d-flex align-items-center mb-1">
<h6 class="mb-0">Format</h6>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="space-x-2">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="format-png" value="png" x-model="$store.canvas.savedImageFormat" />
<label class="form-check-label" for="format-png">png</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="format-jpeg" value="jpeg" x-model="$store.canvas.savedImageFormat">
<label class="form-check-label" for="format-jpeg">jpeg</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="format-webp" value="webp" x-model="$store.canvas.savedImageFormat">
<label class="form-check-label" for="format-webp">webp</label>
</div>
</div>
</div>
<div class="mt-3">
<div class="d-flex align-items-center mb-1">
<h6 class="mb-0" x-text="getTranslatedHTML('quality')"></h6>
<info-popover datatemplateurl="./infoPopover.html" styles="global" shadow="true" class="d-inline-block align-middle ms-1"
data-bs-trigger="hover focus" data-bs-placement="bottom" x-data="infoPopover">
<span slot="info-popover-trigger" class="cursor-pointer">
<i class="fa-solid fa-circle-info fa-md text-warning lh-1"></i>
</span>
<div slot="info-popover-content" x-data="localization">
<small x-html="getTranslatedHTML('qualityApply')"></small>
</div>
</info-popover>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="row align-items-center" >
<div class="col-12" :class="$store.canvas.savedImageFormat === 'png' ? 'inactiveblock':''">
<div class="custom-range-slider">
<input class="w-100 custom-range-slider__control" type="range" id="imageQuality" min="0" max="100" x-model="$store.canvas.savedImageQuality" />
<span class="custom-range-slider__bubble" x-text="$store.canvas.savedImageQuality" :style="{left: setBubblePosition($store.canvas.savedImageQuality, 0, 100)}"></span>
</div>
</div>
<div class="col-12 text-end mt-4 d-flex justify-content-between align-items-center">
<button type="button" class="btn btn-lg btn-primary" @click="$store.canvas.getBundleFileWithSize()">
<span x-text="getTranslatedHTML('calculateImageSize')"></span>
<info-popover datatemplateurl="./infoPopover.html" styles="global" shadow="true" class="d-inline-block align-middle ms-1"
data-bs-trigger="hover focus" data-bs-placement="bottom" x-data="infoPopover">
<span slot="info-popover-trigger" class="cursor-pointer">
<i class="fa-solid fa-circle-info align-middle fa-md"></i>
</span>
<div slot="info-popover-content" x-data="localization">
<small x-html="getTranslatedHTML('recalculationInfo')"></small>
</div>
</info-popover>
</button>
<span x-show="$store.canvas.savedImageFileSize">
<span x-text="$store.canvas.savedImageFileSize < 1 ? ($store.canvas.savedImageFileSize * 1024).toFixed(2) : $store.canvas.savedImageFileSize.toFixed(2)"></span>
<span x-text="$store.canvas.savedImageFileSize < 1 ? 'kb':'mb'"></span>
</span>
</div>
</div>
</div>
</div>
</ul>
</dropdown-menu>
</form>
</div>
<div class="col-12 col-xxxl-5 d-flex justify-content-end">
<button type="button" class="btn editor-interface__square me-2 bg-gray" data-bs-toggle="modal" data-bs-target="#aiModal"
@click="$store.modal.openModal($store.elements.aiModalEL)">
<span class="nav-tabs-alt-border" x-text="getTranslatedHTML('aiTabTitle')"></span>
</button>
<button class="btn editor-interface__square bg-gray me-2" data-bs-toggle="tooltip" x-data="tooltip"
:title="getTranslatedHTML('crop')" :disabled="$store.canvas.isObjectDeselected || $store.canvas.isTextObjectSelected"
@click="$store.canvas.crop($store)">
<i class="fa-solid fa-crop"></i>
</button>
<button @click="$store.canvas.removeWhiteBtn()" class="btn editor-interface__square bg-gray me-2 d-none" data-bs-toggle="tooltip" x-data="tooltip"
:title="getTranslatedHTML('removeWhite')"
:disabled="$store.canvas.isObjectFilterControlsDisabled">
<i class="fa-solid fa-circle-minus"></i>
</button>
<button type="button" class="btn editor-interface__square bg-gray me-2 selected-layer-number flex-shrink-0" data-bs-toggle="tooltip" x-data="tooltip" :title="getTranslatedHTML('layerPositionButtonTitle')"
@click="$dispatch('go-to-tab', {tab: 'layer'})" >
<span x-text="$store.canvas.selectedObjectLayerIndex"></span>
</button>
<button type="button" data-bs-toggle="modal" data-bs-target="#objectDeleteConfirmModal"
@click="$store.modal.openModal($store.elements.deleteObjectModalEL)"
x-data="tooltip" :title="getTranslatedHTML('deleteButtonTitle')"
class="btn btn-danger delete-btn editor-interface__square me-2"
:disabled="$store.canvas.isObjectDeselected">
<i class="fas fa-lg fa-trash"></i>
</button>
<button type="button" class="btn editor-interface__square bg-gray me-2" @click="$store.canvas.setObjectFullSize()"
:title="getTranslatedHTML('fullSizeButtonText')" x-data="tooltip" data-bs-toggle="tooltip" :disabled="$store.canvas.isObjectDeselected">
<span class="bugano-Icon-awesome-expand-arrows-alt"></span>
</button>
<button class="btn editor-interface__square bg-gray me-2" :disabled="$store.canvas.zoomCount >= 10"
@click="$store.canvas.zoomInCanvas()" x-data="tooltip" data-bs-toggle="tooltip" :title="getTranslatedHTML('zoomInTitle')">
<i class="bugano-zoom-in fa-lg"></i>
</button>
<button class="btn editor-interface__square bg-gray me-2" :disabled="$store.canvas.zoomCount <= 1"
@click="$store.canvas.zoomOutCanvas()" x-data="tooltip" data-bs-toggle="tooltip" :title="getTranslatedHTML('zoomOutTitle')">
<i class="bugano-zoom-out fa-lg"></i>
</button>
<!-- <button class="btn editor-interface__square bg-gray me-2" @click="$store.canvas.setFullScreenCanvas();">
<i x-show="!$store.canvas.isFullScreen" class="bugano-Icon-awesome-expand-arrows-alt fa-md"></i>
<i x-show="$store.canvas.isFullScreen" class="fa-solid fa-compress fa-lg"></i>
</button>
<button class="btn editor-interface__square bg-gray me-2" @click="$store.canvas.toPDF()">
<i class="fa fa-file-pdf"></i>
</button>
<button class="btn editor-interface__square bg-gray ms-auto ms-lg-3" @click="$store.canvas.enable3d()">
<span class="fw-bold" >3D</span>
</button> -->
</div>
</div>
<div class="my-2 my-lg-0">
<div class="product-canvas-wrapper">
<div class="card">
<div class="card-body">
<div class="row gy-2 mb-3 gx-1">
<div class="col-3">
<label class="form-label" for="canvas-box-width" x-text="getTranslatedHTML('frameWidth')"></label>
<input id="canvas-box-width" type="number" min="1" :max="$store.canvas.maxCanvasWidth" class="form-control form-control-sm border-primary border-2"
x-model="$store.canvas.canvasWidth" @change="updateCanvasAspectRatio()"/>
</div>
<div class="col-3">
<label class="form-label" for="canvas-box-height" x-text="getTranslatedHTML('frameHeight')"></label>
<input id="canvas-box-height" type="number" min="1" :max="$store.canvas.maxCanvasHeight" class="form-control form-control-sm border-primary border-2"
x-model="$store.canvas.canvasHeight" @change="updateCanvasAspectRatio()" />
</div>
<div class="col">
<label class="form-label" x-text="getTranslatedHTML('sizeTemplateSaveLabel')"></label>
<div class="input-group input-group-sm mb-2">
<input class="form-control form-control-sm border-primary border-2" type="text" id="current-size-preset-label" x-model="currentSizePresetLabel"
:placeholder="getTranslatedHTML('templateName')" autocomplete="off"/>
<button type="button" class="btn btn-sm btn-primary" x-text="getTranslatedHTML('addTitle')" @click="addSizeToTemplate()"></button>
</div>
</div>
</div>
<div class="row gy-2 mb-3 gx-1">
<div class="col-auto">
<div class="form-check form-switch form-check-inline">
<input class="form-check-input" type="checkbox" value="" id="workspace-template" name="workspace-template" x-model="isWorkspaceTemplate" />
<label class="form-check-label" for="workspace-template" x-text="getTranslatedHTML('globalTemplate')"></label>
</div>
</div>
<div class="col" x-show="$store.canvas.sizeTemplates.length || $store.canvas.sizeTemplatesForWorkspace.length">
<label class="form-label" x-text="getTranslatedHTML('templateSelectionDropdownLabel')"></label>
<select class="form-select form-select-sm border" id="image-size-presets" x-model="presetCanvasDim"
@canvas-reset.window="clearSizeTemplates()" @change="updateCanvasDim()"
@canvas-data-restored.window="() => {
populateSizeTemplates($event.detail.sizeTemplates)
}" @canvas-is-ready.window.once="getSizeTemplateForWorkspace()">
<template x-if="$store.canvas.sizeTemplates.length">
<option value="" disabled x-text="getTranslatedHTML('savedPresets')"></option>
</template>
<template x-for="sizeTemplate in $store.canvas.sizeTemplates">
<option :value="`${sizeTemplate.width}x${sizeTemplate.height}`"
x-text="`${sizeTemplate.label} - ${sizeTemplate.width} x ${sizeTemplate.height} px`"></option>
</template>
<template x-if="$store.canvas.sizeTemplatesForWorkspace.length">
<option value="" disabled x-text="getTranslatedHTML('globalPresets')"></option>
</template>
<template x-for="sizeTemplateForWorkspace in $store.canvas.sizeTemplatesForWorkspace">
<option :value="`${sizeTemplateForWorkspace.width}x${sizeTemplateForWorkspace.height}`"
x-text="`${sizeTemplateForWorkspace.label} - ${sizeTemplateForWorkspace.width} x ${sizeTemplateForWorkspace.height} px`"></option>
</template>
</select>
</div>
</div>
</div>
</div>
<div class="ratio ratio-1x1">
<div id="product-canvas-holder" class="d-flex justify-content-center align-items-center">
<canvas id="product-canvas" x-init="() => {
$store.elements.canvasEL = $el;
}"></canvas>
<!-- now this will be not needed as background color is replaced by background image-->
<!-- <img :src="$store.canvas.getSelectedProductObj().productImageUrl" class="img-fluid product-canvas-img" alt="" /> -->
</div>
</div>
<div class="my-3 text-center">
<button type="button" class="btn editor-interface__square bg-gray me-2 historybackbtn" data-bs-toggle="tooltip" x-data="tooltip" :title="getTranslatedHTML('undoTitle')"
x-init="() => {
$store.elements.historyUndo = $el;
}"
@click="canvasHistory.back()">
<i class="fa-solid fa-arrow-rotate-left"></i>
</button>
<button type="button" class="btn editor-interface__square bg-gray me-2 historyforwardbtn" data-bs-toggle="tooltip" x-data="tooltip" :title="getTranslatedHTML('redoTitle')"
x-init ="() => {
$store.elements.historyRedo = $el;
$store.canvas.disableUndoRedoButtons();
}"
@click="canvasHistory.forward()" >
<i class="fa-solid fa-arrow-rotate-right"></i>
</button>
</div>
</div>
</div>
</div>
</div>