mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-17 03:34:56 +02:00
Merge branch 'master' of https://github.com/igorski/bitmappery
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
{
|
||||
"en-US": {
|
||||
"resizeDocument": "Resize document",
|
||||
"width": "Width",
|
||||
"height": "Height",
|
||||
"maintainAspectRatio": "Maintain aspect ratio",
|
||||
"save": "Save",
|
||||
"cancel": "Cancel"
|
||||
|
||||
@@ -27,15 +27,6 @@
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="form" @keyup.enter="save()">
|
||||
<div class="wrapper input">
|
||||
<label v-t="'width'"></label>
|
||||
<input
|
||||
ref="first"
|
||||
v-model.number="width"
|
||||
type="number"
|
||||
name="width"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'maintainAspectRatio'"></label>
|
||||
<toggle-button
|
||||
@@ -43,14 +34,9 @@
|
||||
name="ratio"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'height'"></label>
|
||||
<input
|
||||
v-model.number="height"
|
||||
type="number"
|
||||
name="height"
|
||||
/>
|
||||
</div>
|
||||
<dimensions-formatter
|
||||
v-model="dimensions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
@@ -74,6 +60,7 @@
|
||||
import { mapGetters, mapMutations } from "vuex";
|
||||
import { ToggleButton } from "vue-js-toggle-button";
|
||||
import Modal from "@/components/modal/modal";
|
||||
import DimensionsFormatter from "@/components/ui/dimensions-formatter/dimensions-formatter";
|
||||
import messages from "./messages.json";
|
||||
|
||||
export default {
|
||||
@@ -81,10 +68,13 @@ export default {
|
||||
components: {
|
||||
Modal,
|
||||
ToggleButton,
|
||||
DimensionsFormatter,
|
||||
},
|
||||
data: () => ({
|
||||
width: 0,
|
||||
height: 0,
|
||||
dimensions: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
},
|
||||
ratio: 0,
|
||||
syncLock: false,
|
||||
maintainRatio: true,
|
||||
@@ -93,18 +83,24 @@ export default {
|
||||
...mapGetters([
|
||||
"activeDocument",
|
||||
]),
|
||||
width() {
|
||||
return this.dimensions.width;
|
||||
},
|
||||
height() {
|
||||
return this.dimensions.height;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.width = this.activeDocument.width;
|
||||
this.height = this.activeDocument.height;
|
||||
this.ratio = this.width / this.height;
|
||||
created() {
|
||||
this.dimensions.width = this.activeDocument.width;
|
||||
this.dimensions.height = this.activeDocument.height;
|
||||
this.ratio = this.dimensions.width / this.dimensions.height;
|
||||
|
||||
this.$watch( "width", function( value ) {
|
||||
if ( !this.maintainRatio || this.syncLock ) {
|
||||
return;
|
||||
}
|
||||
this.lockSync();
|
||||
this.height = Math.round( value / this.ratio );
|
||||
this.dimensions.height = Math.round( value / this.ratio );
|
||||
});
|
||||
|
||||
this.$watch( "height", function( value ) {
|
||||
@@ -112,9 +108,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.lockSync();
|
||||
this.width = Math.round( value * this.ratio );
|
||||
this.dimensions.width = Math.round( value * this.ratio );
|
||||
});
|
||||
this.$refs.first.focus();
|
||||
},
|
||||
methods: {
|
||||
...mapMutations([
|
||||
@@ -129,12 +124,12 @@ export default {
|
||||
});
|
||||
},
|
||||
async save() {
|
||||
const { width, height } = this;
|
||||
const { width, height } = this.dimensions;
|
||||
const scaleX = width / this.activeDocument.width;
|
||||
const scaleY = height / this.activeDocument.height;
|
||||
|
||||
await this.resizeActiveDocumentContent({ scaleX, scaleY });
|
||||
this.setActiveDocumentSize({ width, height });
|
||||
this.setActiveDocumentSize({ width: Math.round( width ), height: Math.round( height ) });
|
||||
this.closeModal();
|
||||
},
|
||||
}
|
||||
|
||||
@@ -35,22 +35,9 @@
|
||||
name="name"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'width'"></label>
|
||||
<input
|
||||
v-model.number="width"
|
||||
type="number"
|
||||
name="width"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'height'"></label>
|
||||
<input
|
||||
v-model.number="height"
|
||||
type="number"
|
||||
name="height"
|
||||
/>
|
||||
</div>
|
||||
<dimensions-formatter
|
||||
v-model="dimensions"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions>
|
||||
@@ -74,17 +61,21 @@
|
||||
import { mapGetters, mapMutations } from "vuex";
|
||||
import Modal from "@/components/modal/modal";
|
||||
import DocumentFactory from "@/factories/document-factory";
|
||||
import DimensionsFormatter from "@/components/ui/dimensions-formatter/dimensions-formatter";
|
||||
import messages from "./messages.json";
|
||||
|
||||
export default {
|
||||
i18n: { messages },
|
||||
components: {
|
||||
Modal,
|
||||
DimensionsFormatter,
|
||||
},
|
||||
data: () => ({
|
||||
name : "",
|
||||
width : 1000,
|
||||
height : 1000,
|
||||
dimensions: {
|
||||
width: 1000,
|
||||
height: 1000,
|
||||
},
|
||||
}),
|
||||
computed: {
|
||||
...mapGetters([
|
||||
@@ -102,9 +93,9 @@ export default {
|
||||
]),
|
||||
async save() {
|
||||
this.addNewDocument( DocumentFactory.create({
|
||||
name: this.name,
|
||||
width: this.width,
|
||||
height: this.height
|
||||
name : this.name,
|
||||
width : Math.round( this.dimensions.width ),
|
||||
height : Math.round( this.dimensions.height ),
|
||||
}));
|
||||
this.closeModal();
|
||||
},
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
"newDocument": "New document",
|
||||
"newDocumentNum": "New document #{num}",
|
||||
"name": "Name",
|
||||
"width": "Width",
|
||||
"height": "Height",
|
||||
"create": "Create",
|
||||
"cancel": "Cancel"
|
||||
}
|
||||
|
||||
171
src/components/ui/dimensions-formatter/dimensions-formatter.vue
Normal file
171
src/components/ui/dimensions-formatter/dimensions-formatter.vue
Normal file
@@ -0,0 +1,171 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2021 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
<template>
|
||||
<div>
|
||||
<h3 v-t="'dimensions'" class="title"></h3>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'unit'"></label>
|
||||
<div class="select-combo">
|
||||
<select-box :options="units"
|
||||
v-model="unit"
|
||||
/>
|
||||
<select-box v-if="showDPI"
|
||||
v-model="dpi"
|
||||
:options="dpis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'width'"></label>
|
||||
<input
|
||||
v-model.number="translatedWidth"
|
||||
type="number"
|
||||
name="width"
|
||||
/>
|
||||
</div>
|
||||
<div class="wrapper input">
|
||||
<label v-t="'height'"></label>
|
||||
<input
|
||||
v-model.number="translatedHeight"
|
||||
type="number"
|
||||
name="height"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SelectBox from "@/components/ui/select-box/select-box";
|
||||
import { pixelsToInch, pixelsToCm, pixelsToMm, inchesToPixels, cmToPixels, mmToPixels, } from "@/math/unit-math";
|
||||
import messages from "./messages.json";
|
||||
|
||||
const DPI = [ 72, 97, 150, 300, 600, 1200 ];
|
||||
const UNITS = [ "px", "in", "cm", "mm" ];
|
||||
|
||||
const toFixedFloat = ( value, exp = 2 ) => parseFloat( value.toFixed( exp ));
|
||||
|
||||
export default {
|
||||
i18n: { messages },
|
||||
components: {
|
||||
SelectBox,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
validator: ({ width, height }) => typeof width === "number" && typeof height === "number"
|
||||
},
|
||||
},
|
||||
data: () => ({
|
||||
unit: UNITS[ 0 ],
|
||||
dpi: DPI[ 0 ].toString(),
|
||||
internalValue: {},
|
||||
}),
|
||||
computed: {
|
||||
showDPI() {
|
||||
return this.unit !== "px";
|
||||
},
|
||||
dpis() {
|
||||
return DPI.map( dpi => ({ text: `${dpi} DPI`, value: dpi.toString() }));
|
||||
},
|
||||
units() {
|
||||
return UNITS.map( unit => ({ text: this.$t( unit ), value: unit }));
|
||||
},
|
||||
translatedWidth: {
|
||||
get() {
|
||||
return this.valueFromPx( this.internalValue.width );
|
||||
},
|
||||
set( value ) {
|
||||
this.internalValue.width = this.valueToPx( value );
|
||||
},
|
||||
},
|
||||
translatedHeight: {
|
||||
get() {
|
||||
return this.valueFromPx( this.internalValue.height );
|
||||
},
|
||||
set( value ) {
|
||||
this.internalValue.height = this.valueToPx( value )
|
||||
},
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.internalValue = { ...this.value };
|
||||
},
|
||||
watch: {
|
||||
internalValue( value ) {
|
||||
this.$emit( "input", value );
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
valueFromPx( valueInPx ) {
|
||||
const dpi = parseFloat( this.dpi );
|
||||
switch ( this.unit ) {
|
||||
default:
|
||||
case "px":
|
||||
return valueInPx;
|
||||
case "in":
|
||||
return toFixedFloat( pixelsToInch( valueInPx, dpi ));
|
||||
case "cm":
|
||||
return toFixedFloat( pixelsToCm( valueInPx, dpi ));
|
||||
case "mm":
|
||||
return toFixedFloat( pixelsToMm( valueInPx, dpi ));
|
||||
}
|
||||
},
|
||||
valueToPx( value ) {
|
||||
switch ( this.unit ) {
|
||||
default:
|
||||
case "px":
|
||||
return value;
|
||||
case "in":
|
||||
return inchesToPixels( value, this.dpi );
|
||||
break;
|
||||
case "cm":
|
||||
return cmToPixels( value, this.dpi );
|
||||
break;
|
||||
case "mm":
|
||||
return mmToPixels( value, this.dpi );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "@/styles/_variables";
|
||||
|
||||
.title {
|
||||
color: #FFF;
|
||||
margin: $spacing-medium 0 $spacing-medium 50%;
|
||||
}
|
||||
|
||||
.select-combo {
|
||||
display: inline-flex;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.select-combo .select {
|
||||
flex: 1;
|
||||
margin-right: $spacing-small;
|
||||
}
|
||||
</style>
|
||||
13
src/components/ui/dimensions-formatter/messages.json
Normal file
13
src/components/ui/dimensions-formatter/messages.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"en-US": {
|
||||
"dimensions": "Dimensions",
|
||||
"dpi": "DPI",
|
||||
"width": "Width",
|
||||
"height": "Height",
|
||||
"unit": "Unit",
|
||||
"px": "Pixels",
|
||||
"in": "Inches",
|
||||
"cm": "Centimeters",
|
||||
"mm": "Millimeters"
|
||||
}
|
||||
}
|
||||
31
src/math/unit-math.js
Normal file
31
src/math/unit-math.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Igor Zinken 2021 - https://www.igorski.nl
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to
|
||||
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
const CM_PER_INCH = 2.54;
|
||||
const MM_PER_INCH = CM_PER_INCH * 10;
|
||||
|
||||
export const pixelsToInch = ( pixels, dpi = 72 ) => pixels / dpi;
|
||||
export const pixelsToCm = ( pixels, dpi = 72 ) => pixelsToInch( pixels, dpi ) * CM_PER_INCH
|
||||
export const pixelsToMm = ( pixels, dpi = 72 ) => pixelsToInch( pixels, dpi ) * MM_PER_INCH;
|
||||
export const inchesToPixels = ( inches, dpi = 72 ) => inches * dpi;
|
||||
export const cmToPixels = ( cms, dpi = 72 ) => inchesToPixels( cms / CM_PER_INCH );
|
||||
export const mmToPixels = ( mms, dpi = 72 ) => inchesToPixels( mms / MM_PER_INCH );
|
||||
Reference in New Issue
Block a user