add pinia

This commit is contained in:
Вячеслав Нагель 2023-07-10 00:33:40 +07:00
parent b17852280b
commit 6b59d4962c
Signed by: v9gel
GPG Key ID: 7C2360915BE4C743
8 changed files with 180 additions and 103 deletions

View File

@ -10,6 +10,7 @@
},
"dependencies": {
"html2canvas": "^1.4.1",
"pinia": "^2.1.4",
"vue": "3.3.4"
},
"devDependencies": {

View File

@ -6,31 +6,25 @@ import Button from "./components/Button.vue";
import Page from "./components/Page.vue";
import Input from "./components/Input.vue";
import Canvas from "./components/Canvas.vue";
import { ref } from "vue";
import {
DEFAULT_HORIZONTAL_MARGIN,
DEFAULT_IMAGE_HEIGHT,
DEFAULT_IMAGE_WIDTH,
DEFAULT_VERTICAL_MARGIN,
DEFAULT_IMAGE_BACKGROUND_COLOR,
DEFAULT_VERTICAL_ROTATION,
DEFAULT_MARGIN_ENABLE,
DEFAULT_MARGIN_BACKGROUND_COLOR,
DEFAULT_MARGIN_LEFT_MARGIN,
} from "./const";
import { ButtonSize, InputType } from "./types";
import { useStore } from "./store";
import { storeToRefs } from "pinia";
import { VARIANTS_VERTICAL_ROTATION, VARIANTS_IMAGE_SIZE } from './const';
const vMargin = ref(DEFAULT_VERTICAL_MARGIN);
const vRotation = ref(DEFAULT_VERTICAL_ROTATION);
const hMargin = ref(DEFAULT_HORIZONTAL_MARGIN);
const hMargin2 = ref(DEFAULT_HORIZONTAL_MARGIN);
const imageWidth = ref(DEFAULT_IMAGE_WIDTH);
const imageHeight = ref(DEFAULT_IMAGE_HEIGHT);
const imageBackgroundColor = ref(DEFAULT_IMAGE_BACKGROUND_COLOR);
const store = useStore();
const marginEnable = ref(DEFAULT_MARGIN_ENABLE);
const marginColor = ref(DEFAULT_MARGIN_BACKGROUND_COLOR);
const marginLeftMargin = ref(DEFAULT_MARGIN_LEFT_MARGIN);
const {
imageBackgroundColor,
vMargin,
vRotation,
hMargin,
hMargin2,
marginEnable,
marginColor,
marginLeftMargin,
imageWidth,
imageHeight,
} = storeToRefs(store);
const savePng = async () => {
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
@ -43,90 +37,60 @@ const savePng = async () => {
link.click();
}
};
const rotatePage = () => {
const tmp = imageHeight.value;
imageHeight.value = imageWidth.value;
imageWidth.value = tmp;
};
</script>
<template>
<div class="container">
<Sidebar>
<SidebarBlock title="Фон">
<Input v-model="imageBackgroundColor" :type="InputType.Color">Цвет {{ imageBackgroundColor }}</Input>
<Input v-model="imageBackgroundColor" :type="InputType.Color">Цвет {{ store.imageBackgroundColor }}</Input>
</SidebarBlock>
<SidebarBlock title="Вертикальные линии">
<Input v-model="vMargin" :min="1">Отступ, мм</Input>
<Input v-model="vRotation" :min="0" :max="359">Поворот, градусы</Input>
<ButtonsBar>
<Button :size="ButtonSize.Small" :click="() => {
vRotation = 45;
}
">
45°
</Button>
<Button :size="ButtonSize.Small" :click="() => {
vRotation = 60;
}
">
60°
</Button>
<Button :size="ButtonSize.Small" :click="() => {
vRotation = 65;
}
">
65°
</Button>
<Button :size="ButtonSize.Small" :click="() => {
vRotation = 90;
}
">
90°
<Button v-for="deg in VARIANTS_VERTICAL_ROTATION" :key="deg" :size="ButtonSize.Small" :click="() => store.setVRotation(deg)">
{{ deg }}°
</Button>
</ButtonsBar>
</SidebarBlock>
<SidebarBlock title="Горизонтальные линии">
<Input v-model="hMargin" :min="1">Отступ, мм</Input>
<Input v-model="hMargin2" :min="1">Дополнительный отступ, мм</Input>
</SidebarBlock>
<SidebarBlock title="Поля">
<label class="switch">
<input v-model="marginEnable" type="checkbox">
<span class="slider round"></span>
</label>
<Input v-model="marginColor" :type="InputType.Color">
Цвет {{ marginColor }}
</Input>
<Input v-model="marginLeftMargin" :min="1">Отступ слева, мм</Input>
<div v-if="marginEnable">
<Input v-model="marginColor" :type="InputType.Color">
Цвет {{ marginColor }}
</Input>
<Input v-model="marginLeftMargin" :min="1">Отступ слева, мм</Input>
</div>
</SidebarBlock>
</Sidebar>
<Page>
<Canvas :imageBackgroundColor="imageBackgroundColor" :vMargin="vMargin" :vRotation="vRotation" :hMargin="hMargin"
:hMargin2="hMargin2" :imageWidth="imageWidth" :marginEnable="marginEnable" :marginColor="marginColor"
:marginLeftMargin="marginLeftMargin" :imageHeight="imageHeight" />
<Canvas />
</Page>
<Sidebar horizontalPosition="right">
<SidebarBlock title="Изображение">
<Input v-model="imageHeight" :min="1">Высота, мм</Input>
<Input v-model="imageWidth" :min="1">Ширина, мм</Input>
<ButtonsBar>
<Button :size="ButtonSize.Small" :click="() => {
imageWidth = 148;
imageHeight = 210;
}
">
A5
<Button
v-for="size in VARIANTS_IMAGE_SIZE"
:key="size.name"
:size="ButtonSize.Small"
:click="() => store.setImageSize(size)"
>
{{ size.name }}
</Button>
<Button :size="ButtonSize.Small" :click="() => {
imageWidth = 210;
imageHeight = 297;
}
">
A4
</Button>
<Button :size="ButtonSize.Small" :click="rotatePage">
<Button :size="ButtonSize.Small" :click="store.rotateImage">
Повернуть
</Button>
</ButtonsBar>

View File

@ -1,54 +1,55 @@
<script setup lang="ts">
import { onMounted, onUpdated, ref } from "vue";
import { ONE_MM } from "../const";
import { useStore } from "../store";
import { storeToRefs } from "pinia";
const props = defineProps<{
imageBackgroundColor: string;
const store = useStore();
const {
imageBackgroundColor,
vMargin: number;
vRotation: number;
hMargin: number;
hMargin2: number;
vMargin,
vRotation,
marginEnable: boolean;
marginColor: string;
marginLeftMargin: number;
hMargin,
hMargin2,
imageWidth: number;
imageHeight: number;
}>();
marginEnable,
marginColor,
marginLeftMargin,
imageHeight,
imageWidth,
} = storeToRefs(store);
const ctx = ref<null | CanvasRenderingContext2D>(null);
const clearCanvas = () => {
const { imageWidth, imageHeight, imageBackgroundColor } = props;
if (ctx.value) {
ctx.value.shadowColor
ctx.value.fillStyle = imageBackgroundColor;
ctx.value.fillRect(0, 0, imageWidth * ONE_MM, imageHeight * ONE_MM);
ctx.value.fillStyle = imageBackgroundColor.value;
ctx.value.fillRect(0, 0, imageWidth.value * ONE_MM, imageHeight.value * ONE_MM);
ctx.value.beginPath();
}
};
const render = () => {
const { vMargin, vRotation, hMargin, hMargin2, imageWidth, imageHeight, marginEnable, marginColor, marginLeftMargin } = props;
if (vMargin < 1 || hMargin < 1 || hMargin2 < 1) {
if (vMargin.value < 1 || hMargin.value < 1 || hMargin2.value < 1) {
return
}
if (imageWidth < 1 || imageHeight < 1) {
if (imageWidth.value < 1 || imageHeight.value < 1) {
return
}
clearCanvas();
var hMargins = [hMargin, hMargin2];
var hMargins = [hMargin.value, hMargin2.value];
var imageHeightPx = imageHeight * ONE_MM;
var imageWidthPx = imageWidth * ONE_MM;
var vMarginPx = vMargin * ONE_MM;
var rotationDelta = imageHeightPx / Math.tan(vRotation / 180 * Math.PI);
var imageHeightPx = imageHeight.value * ONE_MM;
var imageWidthPx = imageWidth.value * ONE_MM;
var vMarginPx = vMargin.value * ONE_MM;
var rotationDelta = imageHeightPx / Math.tan(vRotation.value / 180 * Math.PI);
if (ctx.value) {
ctx.value.strokeStyle = "#000000";
@ -83,9 +84,9 @@ const render = () => {
}
x = marginLeftMargin * ONE_MM;
if (marginEnable) {
ctx.value.strokeStyle = marginColor;
x = marginLeftMargin.value * ONE_MM;
if (marginEnable.value) {
ctx.value.strokeStyle = marginColor.value;
ctx.value.lineWidth = 2;
ctx.value.beginPath();
ctx.value.moveTo(x, 0);
@ -112,6 +113,10 @@ onUpdated(() => {
<template>
<canvas id="canvas" :width="imageWidth * ONE_MM" :height="imageHeight * ONE_MM" />
<!-- Костыль, чтобы обновлялся стейт, неужели vue не может красиво это делать -->
<div style="display: none;">
{{ store }}
</div>
</template>
<style scoped>

View File

@ -1,14 +1,31 @@
import { Size } from "./types";
/** 1 миллиметр в пикселях */
export const ONE_MM = 12;
export const DEFAULT_IMAGE_WIDTH = 100;
export const DEFAULT_IMAGE_HEIGHT = 100;
/** Стандартные варианты размера изображения */
export const VARIANTS_IMAGE_SIZE: { [k: string]: Size } = {
A4: {
name: 'A4',
width: 210,
height: 297,
},
A5: {
name: 'A5',
width: 148,
height: 210,
},
};
export const DEFAULT_VERTICAL_MARGIN = 10;
export const DEFAULT_HORIZONTAL_MARGIN = 10;
export const DEFAULT_VERTICAL_ROTATION = 90;
/** Стандартные варианты наклона вертикальных линий */
export const VARIANTS_VERTICAL_ROTATION = [45, 60, 65, 90];
export const DEFAULT_IMAGE_BACKGROUND_COLOR = "#ffffff"
export const DEFAULT_IMAGE_BACKGROUND_COLOR = "#ffffff";
export const DEFAULT_MARGIN_ENABLE = false;
export const DEFAULT_MARGIN_BACKGROUND_COLOR = "#dd3333";

View File

@ -2,5 +2,10 @@ import { createApp } from 'vue'
import './reset.css'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
createApp(App).mount('#app')
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.mount('#app')

61
src/store.ts Normal file
View File

@ -0,0 +1,61 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import {
DEFAULT_HORIZONTAL_MARGIN,
DEFAULT_IMAGE_BACKGROUND_COLOR,
DEFAULT_IMAGE_HEIGHT,
DEFAULT_IMAGE_WIDTH,
DEFAULT_MARGIN_BACKGROUND_COLOR,
DEFAULT_MARGIN_ENABLE,
DEFAULT_MARGIN_LEFT_MARGIN,
DEFAULT_VERTICAL_MARGIN,
DEFAULT_VERTICAL_ROTATION,
} from "./const";
import { Size } from "./types";
export const useStore = defineStore("store", () => {
const imageBackgroundColor = ref(DEFAULT_IMAGE_BACKGROUND_COLOR);
const vMargin = ref(DEFAULT_VERTICAL_MARGIN);
const vRotation = ref(DEFAULT_VERTICAL_ROTATION);
const setVRotation = (newRotation: number) => (vRotation.value = newRotation);
const hMargin = ref(DEFAULT_HORIZONTAL_MARGIN);
const hMargin2 = ref(DEFAULT_HORIZONTAL_MARGIN);
const marginEnable = ref(DEFAULT_MARGIN_ENABLE);
const marginColor = ref(DEFAULT_MARGIN_BACKGROUND_COLOR);
const marginLeftMargin = ref(DEFAULT_MARGIN_LEFT_MARGIN);
const imageWidth = ref(DEFAULT_IMAGE_WIDTH);
const imageHeight = ref(DEFAULT_IMAGE_HEIGHT);
const setImageSize = (size: Size) => {
imageWidth.value = size.width;
imageHeight.value = size.height;
}
const rotateImage = () => {
const tmp = imageHeight.value;
imageHeight.value = imageWidth.value;
imageWidth.value = tmp;
};
return {
imageBackgroundColor,
vMargin,
vRotation,
setVRotation,
hMargin,
hMargin2,
marginEnable,
marginColor,
marginLeftMargin,
imageWidth,
imageHeight,
setImageSize,
rotateImage,
};
});

View File

@ -6,4 +6,10 @@ export enum ButtonSize {
export enum InputType {
Number = 'number',
Color = 'color',
}
}
export interface Size {
name: string;
width: number;
height: number;
}

View File

@ -190,6 +190,11 @@
"@vue/compiler-dom" "3.3.4"
"@vue/shared" "3.3.4"
"@vue/devtools-api@^6.5.0":
version "6.5.0"
resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz#98b99425edee70b4c992692628fa1ea2c1e57d07"
integrity sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==
"@vue/language-core@1.8.0":
version "1.8.0"
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-1.8.0.tgz#3d106afdef859464435c90921fcce200a0b1d15c"
@ -381,6 +386,14 @@ picocolors@^1.0.0:
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
pinia@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.1.4.tgz#a642adfe6208e10c36d3dc16184a91064788142a"
integrity sha512-vYlnDu+Y/FXxv1ABo1vhjC+IbqvzUdiUC3sfDRrRyY2CQSrqqaa+iiHmqtARFxJVqWQMCJfXx1PBvFs9aJVLXQ==
dependencies:
"@vue/devtools-api" "^6.5.0"
vue-demi ">=0.14.5"
postcss@^8.1.10, postcss@^8.4.23:
version "8.4.24"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df"
@ -444,6 +457,11 @@ vscode-uri@^3.0.7:
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8"
integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA==
vue-demi@>=0.14.5:
version "0.14.5"
resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.5.tgz#676d0463d1a1266d5ab5cba932e043d8f5f2fbd9"
integrity sha512-o9NUVpl/YlsGJ7t+xuqJKx8EBGf1quRhCiT6D/J0pfwmk9zUwYkC7yrF4SZCe6fETvSM3UNL2edcbYrSyc4QHA==
vue-template-compiler@^2.7.14:
version "2.7.14"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz#4545b7dfb88090744c1577ae5ac3f964e61634b1"