Vue SVG. Приклад побудови живого параметричного креслення
Отже, на Vue можна робити параметричні креслення у форматі SVG, а потім зберегти як окремий SVG-файл. Що таке параметричне креслення і кому воно потрібно? Уявіть що Ви працюєте закрійником, всі викрійки типові, різні тільки розміри. Було б дуже зручно, якби Ви вбили основні розміри (параметри), а програма сама побудувала б креслення. Так само це стосується інших галузей, де треба завдати декілька параметрів і отримати креслення. Наприклад, Ви займаєтесь виготовленням коробок з картону, тощо. І, щоб скоротити час на креслення і вчасно виконати замовлення клієнта, Вам потрібна система для швидкої побудови креслення.
У цьому прикладі я буду "малювати" статор електричного двигуна.
Переглянути приклад SVG креслення на Vue
Основна компонента, яка займається саме кресленням drawing1 (дивись файл drawing1.js) створюється на базі mixins з назвою vuesvg (дивись файл app-mixins.js). Цей mixin містить (буде містити) всі властивості, методи, тощо, які необхідні всім компонентам - кресленням, які ми будемо створювати (поки-що у прикладі лише один drawing1).
Основна ідея побудови компоненти для SVG креслення така:
- data містить всі параметри, які можуть змінюватись;
- computed - містять всі необхідні розрахунки для побудови креслення. Основним є drawing. Він повертає набір об'єктів, які треба буде намалювати. В даному прикладі drawing повертає набір об'єктів: sectors, lines, circles, sizelines. Кожен з елементів має свої властивості, які використовуються для їх “малювання”.
- зображення креслення будується у шаблоні (template). Також template містить форму для зміни необхідних розмірів.
Зверніть увагу, що для sizelines створений окремий компонент size-lines (дивись файл app-components.js). Саме цей компонент дозволяє простіше встановлювати розмірні лінії на кресленні. Таким чином у template для побудови креслення ви можете використовувати не тільки стандартні SVG теги, але і свої компоненти.
Наприклад, якщо Вам потрібно буде малювати дуже багато гайок, ви можете створити компонент, назвати його, скажімо screw-nuts, який буде малювати гайку певного розміру у певних координатах. У computed властивість drawing додати набір об'єктів типу screw-nuts і заповнити його даними. А у template додати рядок, який буде виводити ваші гайки у SVG, як це зроблено у прикладі з розмірними лініями.
Скачати приклад SVG креслення на Vue
Код самої компоненти:
"use strict";
Vue.component('drawing1', {
mixins:[vuesvg],
data: function () {
return {
pageW: this.pageW,
pageH: this.pageH,
statorDiameter: this.statorDiameter,
statorCentralHoleDiameter: this.statorCentralHoleDiameter,
toothCount: this.toothCount,
toothHeight: this.toothHeight,
toothWidth: this.toothWidth,
toothHatHeight: this.toothHatHeight,
toothBetween: this.toothBetween,
}
},
computed: {
pageCenter: function () {
return {'x':this.pageW/2, 'y': this.pageH/2}
},
/* calculate limits for each parameters. optional */
limits: function () {
return {
statorDiameter: {
min: parseFloat(this.statorCentralHoleDiameter) + this.toothHeight*2,
max: this.pageW
},
statorCentralHoleDiameter: {
min: 0,
max: parseFloat(this.statorDiameter) - this.toothHeight*2
},
toothCount: {
min: 3,
max: 57
},
toothHeight: {
min: this.toothHatHeight * 3,
max: (this.statorDiameter - (this.toothWidth * this.toothCount / Math.PI)) / 2
},
toothWidth: {
min: 1,
max: (this.statorDiameter/2 - this.toothHeight)*2*Math.PI / this.toothCount
},
toothHatHeight: {
min: 1,
max: this.toothHeight/3
},
toothBetween: {
min: 1,
max: ((Math.PI*this.statorDiameter/this.toothCount - this.toothWidth)/2)
}
}
},
drawing: function () {
let result = {'sectors':[],'lines':[],'circles':[],'sizelines':[]}
let radius = this.statorDiameter/2
let sectorAngle = 2*Math.PI/this.toothCount;
let sectorAngleBetween = this.getSectorAngle(radius, this.toothBetween)
let radiusSmall = this.statorDiameter/2 - this.toothHeight
let toothWidthAngle = this.getSectorAngle(radiusSmall, this.toothWidth)
let radiusUnderHat = this.statorDiameter/2 - this.toothHatHeight
let toothWidthAngleHat = this.getSectorAngle(radiusUnderHat, this.toothWidth)
let so_interesting_size_line = {'x1':0,'y1':0,'x2':0,'y2':0}
for (let i = 0; i < this.toothCount; i++) {
let p1 = this.getPointOnCircle(radius, i*sectorAngle + sectorAngleBetween, this.pageCenter)
let p2 = this.getPointOnCircle(radius, (i+1)*sectorAngle - sectorAngleBetween, this.pageCenter)
let p3 = this.getPointOnCircle(radiusUnderHat, i*sectorAngle + sectorAngleBetween, this.pageCenter)
let p4 = this.getPointOnCircle(radiusUnderHat, i*sectorAngle + sectorAngle/2 - toothWidthAngleHat/2, this.pageCenter)
let p5 = this.getPointOnCircle(radiusUnderHat, (i+1)*sectorAngle - sectorAngle/2 + toothWidthAngleHat/2, this.pageCenter)
let p6 = this.getPointOnCircle(radiusUnderHat, (i+1)*sectorAngle - sectorAngleBetween, this.pageCenter)
let p7 = this.getPointOnCircle(radiusSmall, i*sectorAngle, this.pageCenter)
let p8 = this.getPointOnCircle(radiusSmall, i*sectorAngle + sectorAngle/2 - toothWidthAngle/2, this.pageCenter)
let p9 = this.getPointOnCircle(radiusSmall, (i+1)*sectorAngle - sectorAngle/2 + toothWidthAngle/2, this.pageCenter)
let p10 = this.getPointOnCircle(radiusSmall, (i+1)*sectorAngle, this.pageCenter)
result.sectors.push({'p1': p1, 'p2': p2, 'radius': radius})
result.sectors.push({'p1': p7, 'p2': p8, 'radius': radiusSmall})
result.sectors.push({'p1': p9, 'p2': p10, 'radius': radiusSmall})
result.sectors.push({'p1': p3, 'p2': p4, 'radius': radiusUnderHat})
result.sectors.push({'p1': p5, 'p2': p6, 'radius': radiusUnderHat})
result.lines.push({'p1': p1, 'p2': p3})
result.lines.push({'p1': p2, 'p2': p6})
result.lines.push({'p1': p4, 'p2': p8})
result.lines.push({'p1': p5, 'p2': p9})
if (i==this.toothCount-2) { // First tooth
so_interesting_size_line.x1 = p2.x
so_interesting_size_line.y1 = p2.y
}
if (i==this.toothCount-1) { // Last tooth
so_interesting_size_line.x2 = p1.x
so_interesting_size_line.y2 = p1.y
}
}
result.circles.push({'p': this.pageCenter, 'r': this.statorCentralHoleDiameter/2})
/* Size lines */
// Between tooths (so interesting size line)
result.sizelines.push({ 'x1': so_interesting_size_line.x1, 'y1': so_interesting_size_line.y1, 'x2': so_interesting_size_line.x2, 'y2': so_interesting_size_line.y2, 'offset': 5, 'text': this.toothBetween })
// Stator Diameter
result.sizelines.push({ 'x1':this.pageCenter.x - radius, 'y1': this.pageCenter.y, 'x2': this.pageCenter.x + radius, 'y2': this.pageCenter.y, 'offset': (radius * 1.1) + 7, 'text': this.statorDiameter })
// Central Hole Diameter
result.sizelines.push({ 'x1':this.pageCenter.x - this.statorCentralHoleDiameter/2, 'y1': this.pageCenter.y, 'x2': this.pageCenter.x + this.statorCentralHoleDiameter/2, 'y2': this.pageCenter.y, 'offset': 0, 'text': this.statorCentralHoleDiameter })
return result
}
},
mounted: function() {
/* Init values */
this.pageW = 210
this.pageH = 297
this.statorDiameter = 180
this.statorCentralHoleDiameter = 40
this.toothCount = 12
this.toothHeight = 40
this.toothWidth = 20
this.toothBetween = 5
this.toothHatHeight = 3
this.draw()
},
methods: {
getPointOnCircle: function (radius, angle, offset = {'x':0,'y':0}) {
return {'x': offset.x + radius*Math.cos(angle),'y': offset.y + radius*Math.sin(angle)}
},
getSectorAngle: function (radius, length) {
return length / radius
}
},
template: `
<div>
<v-style>
form div {
margin-bottom: 0.5em;
}
form label {
min-width: 15em;
display: inline-block;
}
.drawing {
border: 1px solid silver;
}
</v-style>
<h2>Form:</h2>
<div>
<form>
<div><label>Stator Diameter:</label><input v-model="statorDiameter" type="number" :min="limits.statorDiameter.min" :max="limits.statorDiameter.max"></div>
<div><label>Central Hole's Diameter:</label><input v-model="statorCentralHoleDiameter" type="number" :min="limits.statorCentralHoleDiameter.min" :max="limits.statorCentralHoleDiameter.max"></div>
<div><label>Tooth Count:</label><input v-model="toothCount" type="number" :min="limits.toothCount.min" :max="limits.toothCount.max"></div>
<div><label>Tooth Height:</label><input v-model="toothHeight" type="number" :min="limits.toothHeight.min" :max="limits.toothHeight.max"></div>
<div><label>Tooth Width:</label><input v-model="toothWidth" type="number" :min="limits.toothWidth.min" :max="limits.toothWidth.max"></div>
<div><label>Tooth's Hat Height:</label><input v-model="toothHatHeight" type="number" :min="limits.toothHatHeight.min" :max="limits.toothHatHeight.max"></div>
<div><label>Between Tooths:</label><input v-model="toothBetween" type="number" :min="limits.toothBetween.min" :max="limits.toothBetween.max"></div>
</form>
</div>
<h2>Drawing: <button type="button" @click="saveSvg('vue.svg')">Save SVG-file</button></h2>
<div>
<svg ref="svg" v-if="ready" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" :width="pageW+'mm'" :height="pageH+'mm'" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" :viewBox="'0 0 ' + pageW + ' ' + pageH" xmlns:xlink="http://www.w3.org/1999/xlink" class="drawing">
<defs>
<marker id="DimPoint1" viewBox="-2 -12 29 24" markerWidth="22" markerHeight="18" orient="auto">
<path fill="black" stroke="black" d="M0,0 L20,-4 16,0 20,4 z M0,-10 L0,10 M0,0 L27,0"/>
</marker>
<marker id="DimPoint2" viewBox="-27 -12 29 24" markerWidth="22" markerHeight="18" orient="auto">
<path fill="black" stroke="black" d="M0,0 L-20,-4 -16,0 -20,4 z M0,-10 L0,10 M0,0 L-27,0"/>
</marker>
<v-style type="text/css">
.sizeline { fill: none; stroke-width: 0.2; }
.sizetext { fill: black; stroke: none; font-weight:normal; font-family:'Arial'; font-size: 0.3em; }
</v-style>
</defs>
<g fill="none" stroke="black">
<path v-for="sector in drawing.sectors" :d="'M'+sector.p1.x+','+sector.p1.y+' A'+sector.radius+','+sector.radius+' 0 0,1 ' + sector.p2.x+','+sector.p2.y" />
<line v-for="line in drawing.lines" :x1="line.p1.x" :y1="line.p1.y" :x2="line.p2.x" :y2="line.p2.y" />
<circle v-for="circle in drawing.circles" :cx="circle.p.x" :cy="circle.p.y" :r="circle.r" />
</g>
<size-lines v-for="sizeline in drawing.sizelines" :sizeline="sizeline" />
</svg>
</div>
</div>`
})
Смотри также:
- Vue. Vue-router
- Vue. Свойство data. Основы компонент
- Vue. CRUD. Работа с данными
- Vue. Mixins (Примеси)
- Vue. computed свойства. Постраничный вывод данных
- Vue. Сортировка, поиск данных в таблицах
- Vue. Таблица - трансформер под настольные и мобильные версии
- Vue. Стандартная таблица. Минимизируем код
- Vue. Динамическая загрузка компонент
- Vue. Диалоги и Notifications
- Vue.Стандартный компонент для редактирования данных
- Vue. Разнообразие из кирпичиков
- Flask + Vue. Финальный пример
Недавні записи
- Text to speech. Українська мова
- LCD Display ST7567S (IIC)
- Розпізнавання мови (Speech recognition)
- Selenium
- Комп'ютерний зір (Computer Vision)
- Деякі думки про точність вимірювань в електроприводі
- Датчики Холла 120/60 градусів
- Модуль драйверів напівмосту IGBT транзисторів
- Драйвер IGBT транзисторів на A316J
- AS5600. Варіант встановлення на BLDC мотор
Tags
docker sensors rtc led timer wifi remap sms css websocket mongodb bme280 rfid bluetooth esp8266 nodemcu bkp piezo bmp280 i2c mpu-6050 encoder examples avr brushless ngnix nvic displays bldc java-script pmsm barometer pwm lcd ethernet stm32 raspberry-pi capture usart gpio exti atmega meteo solar smd dma adc eeprom eb-500 flask python ssd1306 uart options flash mpx4115a html gps st-link 3d-printer servo dc-dc foc git books battery hih-4000 watchdog dht11 web tim programmator ssd1331 mpu-9250 usb rs-232 motor max1674 soldering
Архіви