init
This commit is contained in:
+61
@@ -0,0 +1,61 @@
|
||||
<script setup lang="ts">
|
||||
import Sidebar from './components/Sidebar.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 } from './const';
|
||||
|
||||
const vMargin = ref(DEFAULT_VERTICAL_MARGIN)
|
||||
const hMargin = ref(DEFAULT_HORIZONTAL_MARGIN)
|
||||
const imageWidth = ref(DEFAULT_IMAGE_WIDTH)
|
||||
const imageHeight = ref(DEFAULT_IMAGE_HEIGHT)
|
||||
|
||||
const savePng = async () => {
|
||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||
|
||||
if (canvas) {
|
||||
const image = canvas.toDataURL("image/png")
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("download", "image.png");
|
||||
link.href = image
|
||||
link.click();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container">
|
||||
<Sidebar>
|
||||
<Input v-model="hMargin">Отступ для горизонтальных линий, мм</Input>
|
||||
<Input v-model="vMargin">Отступ для вертикальных линии, мм</Input>
|
||||
<Input v-model="imageWidth">Высота изображения, мм</Input>
|
||||
<Input v-model="imageHeight">Ширина изображения, мм</Input>
|
||||
<button @click="savePng">Сохранить png</button>
|
||||
</Sidebar>
|
||||
<Page>
|
||||
<Canvas :vMargin="vMargin" :hMargin="hMargin" :imageWidth="imageWidth" :imageHeight="imageHeight" />
|
||||
</Page>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
#app,
|
||||
body,
|
||||
html {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: aqua;
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, onUpdated, ref } from "vue";
|
||||
import { ONE_MM } from "../const";
|
||||
|
||||
const props = defineProps<{
|
||||
vMargin: number;
|
||||
hMargin: number;
|
||||
imageWidth: number;
|
||||
imageHeight: number;
|
||||
}>();
|
||||
|
||||
const ctx = ref<null | CanvasRenderingContext2D>(null);
|
||||
|
||||
const clearCanvas = () => {
|
||||
const { imageWidth, imageHeight } = props;
|
||||
|
||||
if (ctx.value) {
|
||||
ctx.value.clearRect(0, 0, imageWidth * ONE_MM, imageHeight * ONE_MM);
|
||||
}
|
||||
};
|
||||
|
||||
const render = () => {
|
||||
const { vMargin, hMargin, imageWidth, imageHeight } = props;
|
||||
|
||||
clearCanvas();
|
||||
|
||||
if (ctx.value) {
|
||||
for (let i = 1; i < 100; i++) {
|
||||
const y = i * hMargin * ONE_MM;
|
||||
ctx.value.moveTo(0, y);
|
||||
ctx.value.lineTo(imageWidth * ONE_MM, y);
|
||||
ctx.value.stroke();
|
||||
}
|
||||
|
||||
for (let i = 1; i < 100; i++) {
|
||||
const x = i * vMargin * ONE_MM;
|
||||
ctx.value.moveTo(x, 0);
|
||||
ctx.value.lineTo(x, imageHeight * ONE_MM);
|
||||
ctx.value.stroke();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const canvas = document.getElementById("canvas") as HTMLCanvasElement;
|
||||
|
||||
if (canvas) {
|
||||
ctx.value = canvas.getContext("2d");
|
||||
}
|
||||
|
||||
render();
|
||||
});
|
||||
|
||||
onUpdated(() => {
|
||||
render();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<canvas
|
||||
id="canvas"
|
||||
:width="imageWidth * ONE_MM"
|
||||
:height="imageHeight * ONE_MM"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
canvas {
|
||||
border: 1px solid black;
|
||||
margin: 20px;
|
||||
height: calc(100vh - 40px);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,23 @@
|
||||
<script setup lang="ts">
|
||||
const modelValue = defineModel();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="input">
|
||||
<label>
|
||||
<slot></slot>
|
||||
</label>
|
||||
<input type="number" v-model="modelValue" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.input {
|
||||
margin: 10px;
|
||||
|
||||
}
|
||||
|
||||
input {
|
||||
border: #333 1px solid;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot></slot>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="sidebar">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 330px;
|
||||
background-color: white;
|
||||
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
|
||||
z-index: 2;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,8 @@
|
||||
/** 1 миллиметр в пикселях */
|
||||
export const ONE_MM = 12;
|
||||
|
||||
export const DEFAULT_IMAGE_WIDTH = 210;
|
||||
export const DEFAULT_IMAGE_HEIGHT = 297;
|
||||
|
||||
export const DEFAULT_VERTICAL_MARGIN = 10;
|
||||
export const DEFAULT_HORIZONTAL_MARGIN = 10;
|
||||
@@ -0,0 +1,5 @@
|
||||
import { createApp } from 'vue'
|
||||
import './reset.css'
|
||||
import App from './App.vue'
|
||||
|
||||
createApp(App).mount('#app')
|
||||
@@ -0,0 +1,93 @@
|
||||
/* Reset and base styles */
|
||||
* {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Links */
|
||||
|
||||
a, a:link, a:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Common */
|
||||
|
||||
aside, nav, footer, header, section, main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6, p {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
ul, ul li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img, svg {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
address {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
|
||||
input, textarea, button, select {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
color: inherit;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
|
||||
button, input[type="submit"] {
|
||||
display: inline-block;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:focus, input:active,
|
||||
button:focus, button:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#app {
|
||||
width: 100vh;
|
||||
}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user