docs: add readme and license, translate to english

This commit is contained in:
2026-06-23 22:53:53 +05:00
parent 35fec72f9f
commit 56f01d412e
5 changed files with 93 additions and 16 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Ku6epXBOCTuK
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.
+56
View File
@@ -0,0 +1,56 @@
# CSS Matrix3D Perspective Calculator
A browser-based tool for generating CSS `matrix3d()` transforms with perspective projection. Define 4 corners on a field and a widget size — get ready-to-use CSS code.
**Keywords:** CSS matrix3d, perspective transform, 3D CSS, homography, quadrilateral mapping, CSS transform generator, perspective projection, CSS 3D effects
## Usage
Open `index.html` in a browser (no build step required).
- **Edit** — drag corners on the SVG field, adjust field and widget sizes
- **Preview** — see the live matrix3d() transform result
Copy the CSS from the "CSS Output" section and paste into your project.
## Features
- Pure math — homography-based matrix3d() calculation
- Live preview with real-time updates
- Drag-and-drop corner positioning
- Editable field dimensions with aspect ratio lock
- Rescale coordinates option when resizing the field
- Zero dependencies, runs in any modern browser
## Use Cases
- **Presentations & landing pages** — render a widget or screenshot on a monitor at any angle
- **Carousels & galleries** — show product cards with perspective deformation
- **Interactive infographics** — overlay data on maps or charts with perspective
- **App previews** — place mobile UI on a device background
- **Scroll-based effects** — animate matrix3d for 3D effects without WebGL
- **Mockups & prototypes** — visually place interfaces in context (on a wall, laptop, banner)
## Project Structure
```text
├── index.html
├── style.css
└── js/
├── main.js — entry point
├── state.js — state management
├── homography.js — homography math
├── svg-renderer.js — SVG rendering
├── drag.js — drag handling
├── inputs.js — input fields
├── preview.js — live preview
└── ui.js — buttons and modes
```
## How It Works
The tool uses [homography](https://en.wikipedia.org/wiki/Homography_(computer_vision)) to compute a 3D perspective transform from 4 corresponding points. The resulting `matrix3d()` CSS property maps a rectangular widget onto an arbitrary quadrilateral defined by the user.
## License
MIT
+12 -12
View File
@@ -1,5 +1,5 @@
<!doctype html> <!doctype html>
<html lang="ru"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -10,9 +10,9 @@
<header> <header>
<div class="header-left"> <div class="header-left">
<h1>CSS Matrix3D Perspective Calculator</h1> <h1>CSS Matrix3D Perspective Calculator</h1>
<p>Задайте 4 точки на поле и размер виджета — получите CSS matrix3d() для перспективного отображения</p> <p>Set 4 corners on the field and widget size — get CSS matrix3d() for perspective rendering</p>
</div> </div>
<span class="mode-hint" id="modeHint">Перетаскивайте углы на поле</span> <span class="mode-hint" id="modeHint">Drag corners on the field</span>
<div class="mode-toggle"> <div class="mode-toggle">
<button class="mode-btn active" id="btnEdit">Edit</button> <button class="mode-btn active" id="btnEdit">Edit</button>
<button class="mode-btn" id="btnPreview">Preview</button> <button class="mode-btn" id="btnPreview">Preview</button>
@@ -38,8 +38,8 @@
<div class="right-col"> <div class="right-col">
<div class="panel"> <div class="panel">
<div class="panel-header"> <div class="panel-header">
<h2>Основное поле</h2> <h2>Field</h2>
<button class="btn btn-edit-field" id="btnEditField">Редактировать</button> <button class="btn btn-edit-field" id="btnEditField">Edit</button>
</div> </div>
<div class="field-size-stack"> <div class="field-size-stack">
<div class="chain-link" id="chainLink" title="Keep aspect ratio"> <div class="chain-link" id="chainLink" title="Keep aspect ratio">
@@ -68,17 +68,17 @@
<div class="field-edit-options" id="fieldEditOptions"> <div class="field-edit-options" id="fieldEditOptions">
<label class="checkbox-label"> <label class="checkbox-label">
<input type="checkbox" id="chkRescaleCoords" checked /> <input type="checkbox" id="chkRescaleCoords" checked />
<span>Пересчитать координаты</span> <span>Rescale coordinates</span>
</label> </label>
<div class="field-save-row"> <div class="field-save-row">
<button class="btn btn-cancel-field" id="btnCancelField">Отменить</button> <button class="btn btn-cancel-field" id="btnCancelField">Cancel</button>
<button class="btn btn-save-field" id="btnSaveField">Сохранить</button> <button class="btn btn-save-field" id="btnSaveField">Save</button>
</div> </div>
</div> </div>
</div> </div>
<div class="panel"> <div class="panel">
<h2>Виджет</h2> <h2>Widget</h2>
<div class="field-row"> <div class="field-row">
<label><span>W</span><input id="inpWidgetW" type="number" value="400" min="1" /></label> <label><span>W</span><input id="inpWidgetW" type="number" value="400" min="1" /></label>
<label><span>H</span><input id="inpWidgetH" type="number" value="300" min="1" /></label> <label><span>H</span><input id="inpWidgetH" type="number" value="300" min="1" /></label>
@@ -86,7 +86,7 @@
</div> </div>
<div class="panel"> <div class="panel">
<h2>Углы четырёхугольника</h2> <h2>Corners</h2>
<div class="corner-inputs" id="cornerInputs"></div> <div class="corner-inputs" id="cornerInputs"></div>
</div> </div>
@@ -94,8 +94,8 @@
<h2>CSS Output</h2> <h2>CSS Output</h2>
<textarea id="cssOutput" rows="10" readonly></textarea> <textarea id="cssOutput" rows="10" readonly></textarea>
<div class="btn-row"> <div class="btn-row">
<button class="btn btn-copy" id="copyBtn">Копировать CSS</button> <button class="btn btn-copy" id="copyBtn">Copy CSS</button>
<button class="btn btn-reset" id="resetBtn">Сброс</button> <button class="btn btn-reset" id="resetBtn">Reset</button>
</div> </div>
</div> </div>
</div> </div>
+1 -1
View File
@@ -28,7 +28,7 @@ export function computeHomography(state) {
} }
export function toMatrix3dCSS(H) { export function toMatrix3dCSS(H) {
if (!H) return '/* вырожденная конфигурация */'; if (!H) return '/* degenerate configuration */';
const n = v => parseFloat(v.toFixed(8)); const n = v => parseFloat(v.toFixed(8));
return [ return [
'transform-origin: 0 0;', 'transform-origin: 0 0;',
+3 -3
View File
@@ -50,8 +50,8 @@ function setMode(mode) {
$('btnEdit').classList.toggle('active', mode === 'edit'); $('btnEdit').classList.toggle('active', mode === 'edit');
$('btnPreview').classList.toggle('active', mode === 'preview'); $('btnPreview').classList.toggle('active', mode === 'preview');
$('modeHint').textContent = mode === 'edit' $('modeHint').textContent = mode === 'edit'
? 'Перетаскивайте углы на поле' ? 'Drag corners on the field'
: 'Результат matrix3d() трансформации'; : 'matrix3d() transform result';
} }
export function updateCSSOutput(state) { export function updateCSSOutput(state) {
@@ -139,7 +139,7 @@ export function initUI(state, updateAll) {
navigator.clipboard.writeText(text).then(() => { navigator.clipboard.writeText(text).then(() => {
const btn = $('copyBtn'); const btn = $('copyBtn');
const orig = btn.textContent; const orig = btn.textContent;
btn.textContent = 'Скопировано!'; btn.textContent = 'Copied!';
setTimeout(() => btn.textContent = orig, 1500); setTimeout(() => btn.textContent = orig, 1500);
}); });
}); });