使HTML元素在保持长宽比的情况下随浏览器大小缩放



我卖的是A4纸的贴纸,有很多顾客问我"特定尺寸"的贴纸可以买多少。我正在制作一个页面,你可以输入长度和宽度的贴纸,它会计算多少贴纸的长度和宽度可以适合在A4纸上。板材尺寸为210x297mm。我总是允许在所有设计上有2mm的边框,每个标志之间有2mm的空间。一切正常。除了你调整窗口高度的部分。床单不能保持原样。我想让工作表的宽度和高度一起缩放

我不知道如何使用变换缩放在这种情况下,所以我试着只是改变高度和宽度,使其一起缩放。高度会缩放,但宽度会保持不变直到你在移动设备上改变宽度。当你预览HTML并调整浏览器大小后,你就会明白我的意思了。

这是一个片段的麻烦的部分在调整大小的函数,我从哪里挣扎。

function resize() {
setTimeout(() => {
sheet: {
sheetWidthPX = inToPX(sheetWidth), newWidth = sheetWidthPX * (document.querySelector('.right').clientWidth / sheetWidthPX) - 60;
sheetHeightPX = inToPX(sheetHeight), newHeight = sheetHeightPX * (document.documentElement.clientHeight / sheetHeightPX) - 60; // 60px extra space
// stickersContainer.style.setProperty('--stickersWidth', newWidth + 'px');
stickersContainer.style.setProperty('--stickersWidth', '555px');
stickersContainer.style.setProperty('--stickersHeight', newHeight + 'px');
}
stickers: {
logo = stickersContainer.querySelector('.logo');
firstSticker = stickers.querySelector('.sticker');
parentWidth = stickersContainer.offsetWidth;
parentHeight = stickersContainer.offsetHeight - logo.offsetHeight;
width = firstSticker.offsetWidth, height = firstSticker.offsetHeight;
// newWidth = stickers-width * (sheet-width / (stickers-width * l)) - spacings * (how many gaps)
newWidth = width * (parentWidth / (width * stickersPerRow)) - inToPX(spacing) * 2;
newHeight = height * (parentHeight / (height * stickersPerCol)) - inToPX(spacing) * 2;
stickersContainer.style.setProperty('--stickerWidth', newWidth + 'px');
stickersContainer.style.setProperty('--stickerHeight', newHeight + 'px');
}
}, 0);
}

我的脑子被数学弄得焦头烂脑。也许有更好的方法来做每件事?如果没有,也许我应该考虑学习帆布?这是完整的JavaScript, CSS和HTML

addEventListener('DOMContentLoaded', () => {
const stickerWidth = 60;
const stickerHeight = 40;
const sheetWidth = 210;
const sheetHeight = 297;
const border = 2;
const spacing = 3;
const stickersContainer = document.querySelector('.stickersContainer');
const stickers = stickersContainer.querySelector('.stickers');
const inToPX = n => n * 3.7795275591; // default unit is mm
function toMM(n, unit) {
switch (unit || (document.querySelector('input[name="unit"]:checked')?.value || 'mm').toLowerCase()) {
case "in": return n *= 25.4;
case "cm": return n *= 10;
case "px": return n /= 3.7795275591;
default: return n;
}
}
function displayStickers(length, width, unit = 'mm') {
length = toMM(length, unit);
width = toMM(width, unit);
stickersContainer.style.setProperty('--opacity', 0);
// Calculate the number of stickers that can fit on the sheet in each direction
stickersPerRow = Math.floor((sheetWidth - border) / (length + spacing));
stickersPerCol = Math.floor((sheetHeight - border) / (width + spacing));
// Calculate the total number of stickers that can fit on the sheet
const totalStickers = stickersPerRow * stickersPerCol;
stickers.style.gridTemplateColumns = `repeat(${stickersPerRow}, ${length}fr)`;
// stickers.style.gridTemplateRows = `repeat(${stickersPerCol}, ${width}mm)`;
stickers.style.gap = `${spacing}mm`;
stickers.style.pdding = `${spacing}mm`;
stickers.innerHTML = '';
resize();
stickersContainer.style.setProperty('--stickerWidth', length + 'mm');
stickersContainer.style.setProperty('--stickerHeight', width + 'mm');
setTimeout(() => stickersContainer.style.removeProperty('--opacity'), 0);
for (let i = 0; i < totalStickers; i++) {
const sticker = document.createElement('div');
sticker.classList.add('sticker');
stickers.appendChild(sticker);
}
document.querySelector('.count').textContent = totalStickers;
}
function resize() {
setTimeout(() => {
sheet: {
sheetWidthPX = inToPX(sheetWidth), newWidth = sheetWidthPX * (document.querySelector('.right').clientWidth / sheetWidthPX) - 60;
sheetHeightPX = inToPX(sheetHeight), newHeight = sheetHeightPX * (document.documentElement.clientHeight / sheetHeightPX) - 60; // 60px extra space
// stickersContainer.style.setProperty('--stickersWidth', newWidth + 'px');
stickersContainer.style.setProperty('--stickersWidth', '555px');
stickersContainer.style.setProperty('--stickersHeight', newHeight + 'px');
}
stickers: {
logo = stickersContainer.querySelector('.logo');
firstSticker = stickers.querySelector('.sticker');
parentWidth = stickersContainer.offsetWidth;
parentHeight = stickersContainer.offsetHeight - logo.offsetHeight;
width = firstSticker.offsetWidth, height = firstSticker.offsetHeight;
// newWidth = stickers-width * (sheet-width / (stickers-width * l)) - spacings * (how many gaps)
newWidth = width * (parentWidth / (width * stickersPerRow)) - inToPX(spacing) * 2;
newHeight = height * (parentHeight / (height * stickersPerCol)) - inToPX(spacing) * 2;
stickersContainer.style.setProperty('--stickerWidth', newWidth + 'px');
stickersContainer.style.setProperty('--stickerHeight', newHeight + 'px');
}
}, 0);
}
const editableElements = document.querySelectorAll('[contenteditable]');
const unitElements = document.querySelectorAll('[contenteditable]+.unitText');
for (const unitText of unitElements) {
const editableElement = unitText.previousElementSibling;
unitText.addEventListener('click', e => {
editableElement.focus();
range = document.createRange();
range.selectNodeContents(editableElement);
range.collapse(false);
selection = getSelection();
selection.removeAllRanges();
selection.addRange(range);
});
editableElement.addEventListener('keydown', e => {
if (e.key == 'Enter' || !(e.key.length > 1 || !/[^.d]/g.exec(e.key)))
e.preventDefault();
oldUnit = document.querySelector('input[name="unit"]:checked').value;
if (editableElement == unitElements[0].previousElementSibling)
oldWidth = parseFloat(editableElement.textContent.replace(/[^.d]/g, ''));
else if (editableElement == unitElements[1].previousElementSibling)
oldHeight = parseFloat(editableElement.textContent.replace(/[^.d]/g, ''));
});
editableElement.addEventListener('input', e => {
const newValue = parseFloat(e.target.textContent.replace(/[^.d]/g, '') + e.key);
const unit = document.querySelector('input[name="unit"]:checked').value;
if ((!newValue && newValue != 0) || newValue < 0) {
e.target.textContent = 0;
e.target.focus();
return document.execCommand('selectAll', false, null);
}
if (e.target == unitElements[0].previousElementSibling && toMM(newValue) > sheetWidth) {
e.target.contentEditable = false;
var msg = `The width of the sheet is ${sheetWidth}mm. A ${newValue}${unit}${unit !== 'mm' ? ` (or ${toMM(newValue, unit)}mm) ` : ' '}width would be above it.`;
document.body.classList.add('calculations-invalid');
} else if (e.target == unitElements[1].previousElementSibling && toMM(newValue) > sheetHeight) {
e.target.contentEditable = false;
var msg = `The height of the sheet is ${sheetHeight}mm. A ${newValue}${unit}${unit !== 'mm' ? ` (or ${toMM(newValue, unit)}mm) ` : ' '}height would be above it.`;
document.body.classList.add('calculations-invalid');
} else {
e.target.contentEditable = true;
msg = false;
}
if (!msg)
document.body.classList.remove('calculations-invalid', 'invalid');
else {
setTimeout(() => {
e.target.contentEditable = true;
e.target.focus();
if (!document.body.classList.contains('invalid')) {
alert(msg);
document.execCommand('selectAll', false, null);
document.body.classList.add('invalid');
}
}, 0);
}
});
}
oldUnit = document.querySelector('input[name="unit"]:checked').value;
oldWidth = parseFloat(editableElements[0].textContent.replace(/[^.d]/g, ''));
oldHeight = parseFloat(editableElements[1].textContent.replace(/[^.d]/g, ''));
for (const checkbox of document.querySelectorAll('input[name="unit"]'))
checkbox.addEventListener('change', e => {
const unit = e.target.value
for (const unitText of document.querySelectorAll('.unitText'))
unitText.textContent = unit;
});
function calculate() {
let [width, height] = document.querySelectorAll('[role="textbox"][contenteditable]');
let unit = document.querySelector('input[name="unit"]:checked').value;
width = parseFloat(width.textContent.replace(/[^.d]/g, ''));
height = parseFloat(height.textContent.replace(/[^.d]/g, ''));
if (toMM(width) > sheetWidth)
return alert(`The width of the sheet is ${sheetWidth}mm. ${width}${unit} ${unit !== 'mm' ? `(or ${toMM(width, unit)}mm)` : ''} is too much.`);
if (toMM(height) > sheetHeight)
return alert(`The height of the sheet is ${sheetHeight}mm. ${height}${unit} ${unit !== 'mm' ? `(or ${toMM(height, unit)}mm)` : ''} is too much.`);
console.log(width, height, unit);
displayStickers(width, height, unit);
}
addEventListener('resize', resize);
displayStickers(stickerWidth, stickerHeight, 'mm');
document.querySelector('button.calculate').addEventListener('click', calculate);
document.querySelector('input[name="unit"]:checked')?.dispatchEvent(new Event('change'));
});
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap');
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
font-size: 16px;
color: var(#333);
height: 100vh;
font-family: 'Roboto', sans-serif;
}
main {
/* display: grid; */
/* grid-template-columns: 1fr 1fr; */
gap: 20px;
display: flex;
justify-content: space-around;
justify-content: space-evenly;
padding: 20px;
height: 100vh;
}
.left {
display: grid;
place-content: center;
text-align: center;
}
section.left :is(button, a) {
all: unset;
background: #e84e1c;
color: #fff;
border-radius: 4px;
padding: 3px 10px;
cursor: pointer;
}
section.left button.calculate {
padding: 2px 10px;
margin: 6% 0;
}
section.left a {
padding: 3px 2px;
}
body[class$=invalid] section.left button.calculate {
opacity: .5;
cursor: not-allowed;
pointer-events: none;
}
.left>.leftContent {
transform: translateY(-5%);
}
.stickersContainer {
width: var(--stickersWidth, 205mm);
height: var(--stickersHeight, 260mm);
/* width: 40vw; */
/* height: 90vh; */
margin: 0 auto;
display: grid;
opacity: var(--opacity, 1);
/* place-content: center; */
--border-radius: 10px;
border-radius: var(--border-radius);
border: 1px solid #ddd;
grid-template-rows: 8%;
/* padding: 0 6mm 6mm 6mm; */
/* padding: 0 6mm 8% 6mm; */
padding: 0 6mm 0mm 6mm;
}
.stickers {
padding-bottom: 8%;
}
.stickersContainer>.logo {
display: grid;
}
.stickersContainer>.logo img {
width: 17%;
place-self: center;
align-self: center;
}
@-moz-document url-prefix() {
.stickersContainer>.logo img {
margin: 25px 0;
}
.stickers {
padding-bottom: 3.5%;
}
}
.stickers {
display: grid;
place-content: center;
place-items: center;
}
.stickers .sticker {
border: 2px solid #e84f1d;
border: 2px solid #e84f1dab;
border-radius: var(--border-radius);
width: var(--stickerWidth, 60mm);
height: var(--stickerHeight, 40mm);
}
.value {
border: 1px solid #7e7e7c;
display: inline-flex;
border-radius: 6px;
margin-left: 3px;
padding: 1.6px 8px;
font-size: 80%;
}
.value .input {
display: inline-table;
border-radius: 3px;
margin-left: 5px;
margin: 0;
}
.value .input::after {
content: attr(data-after);
opacity: .5;
}
.value .input:empty {
width: 20px;
}
[role="textbox"]:focus-within {
outline: none;
}
.messures {
display: grid;
gap: 10px;
font-size: 120%;
}
.messures .width {
margin-left: 8px;
}
.left .units {
margin: 1% 0 10% 0;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
@media screen and (max-width: 960px) {
main {
display: block;
}
main .left {
margin: 20px 0 50px 0;
}
}
@media screen and (max-width: 600px) {
main .stickersContainer {
width: 100%;
}
}
[contenteditable] {
-webkit-user-select: text;
user-select: text;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<main>
<section class="left">
<div class="leftContent">
<div>Please enter the dimentions of your shape</div>
<div class="units">
<span><input type="radio" name="unit" value="mm" checked autocomplete="off">MM</span>
<span><input type="radio" name="unit" value="cm" autocomplete="off">CM</span>
<span><input type="radio" name="unit" value="in" autocomplete="off">Inches</span>
</div>
<div class="messures">
<div>
<label for="width">Width <span>(</span><span class="unitText">mm</span>)</label>
<span class="value">
<span class="input" role="textbox" contenteditable>60</span>
<span class="unitText">mm</span>
</span>
</div>
<div>
<label for="height">Height (<span class="unitText">mm</span>)</label>
<span class="value">
<span class="input" role="textbox" contenteditable>40</span>
<span class="unitText">mm</span>
</span>
</div>
</div>
<button class="calculate">Calculate</button>
<div>At these dimentions we can fit</div>
<div><span class="count"></span> stickers per sheet</div>
<div style="margin-top: 9%;">To order click <a href="">here</a></div>
</div>
</section>
<section class="right">
<div class="rightContent">
<div class="stickersContainer">
<div class="logo">
<img class="regular-logo"
src="img.jpg"
alt="Stickers On Sheet">
</div>
<div class="stickers"></div>
</div>
</div>
</section>
</main>
</body>
</html>

如果你手动打开它们,你可能会更好地理解这个问题,而不是通过StackOverflow,因为它使一个小窗口

我将在计算时使用以下逻辑:

  • 使用响应式aspect-ratio的响应式定心技巧
  • 在A4纸上使用3mm安全填充物
  • 计算给定单元格大小的行和颜色(W/Hmm) -不要忘记所需的间距(2mm边框和间距在单元格的两侧!)
  • 确定tot cell
  • 一旦你创建了N*N网格,你所需要做的就是计算单元格宽度的百分比,并为该单元格设置所需的长宽比
  • 不要忘记,网格的gapspacing也需要计算和应用百分比%值-为了响应

例子:

// DOM utility functions:
const el = (sel, par) => (par || document).querySelector(sel);
const els = (sel, par) => (par || document).querySelectorAll(sel);
const elNew = (tag, prop) => Object.assign(document.createElement(tag), prop);
const css = (el, styles) => typeof styles === "object" ? Object.assign(el.style, styles) : el.style.cssText = styles;

// Utility functions:
const repeat = (n, cb) => [...Array(n)].forEach((_, i) => cb(i))
const toMM = (n = 0, unit = "in") => ({in: n * 25.4, cm: n * 10, px: n / 3.7795275591}[unit.toLowerCase()] ?? n);
// App:
const elSheet = el("#sheet");
const elCount = el("#count");
const elsUnit = els("[name='unit']");
const elsUnitSymb = els(".unit");
const elWidth = el("#width");
const elHeight = el("#height");
// Default values in "mm" units:
const sheetWidthMM = 210;
const sheetHeightMM = 297;
const borderMM = 2;
const spacingMM = 3;
const displayStickers = () => {

const unit = [...elsUnit].filter(elUnit => elUnit.checked)[0].value ?? "mm";
const widthMM = toMM(Number(elWidth.value) ?? 0, unit);
const heightMM = toMM(Number(elHeight.value) ?? 0, unit);
const rows = Math.floor((sheetHeightMM - spacingMM * 2) / (heightMM + borderMM * 2 + spacingMM));
const cols = Math.floor((sheetWidthMM - spacingMM * 2) / (widthMM + borderMM * 2 + spacingMM));
const totalStickers = rows * cols;
const spacingPct = spacingMM / sheetWidthMM * 100;
const gridWidthMM = (sheetWidthMM - spacingMM * 2);
const colWidthMM = gridWidthMM / cols;
const stickerWidthPct = widthMM / colWidthMM  * 100;
const stickerRatio = widthMM / heightMM;

elSheet.style.setProperty("--stickerWidth", stickerWidthPct);
elSheet.style.setProperty("--stickerRatio", stickerRatio);
css(elSheet, {
gridTemplateColumns: `repeat(${cols}, ${1}fr)`,
gridTemplateRows: `repeat(${rows}, ${1}fr)`,
gap: `${spacingPct}%`,
padding: `${spacingPct}%`,
});
elSheet.innerHTML = "";
repeat(totalStickers, () => elSheet.append(elNew("div", {className: "sticker"})));

elCount.textContent = totalStickers;

elsUnitSymb.forEach(elUnitSymb => elUnitSymb.textContent = unit);
};
[...elsUnit, elWidth, elHeight].forEach((elInp) =>
elInp.addEventListener("input", displayStickers));
displayStickers();
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap');
* {
margin: 0;
box-sizing: border-box;
}
html,
body {
height: 100vh;
}
p {
padding: 0.2rem 0;
}
main {
display: flex;
width: 100%;
height: 100%;
}
#panel {
background: #eee;
padding: 2rem;
display: flex;
flex-direction: column;
min-width: 240px;
width: 20%;
justify-content: center;
}
#panel input[type="text"] {
width: 50px;
}
#area {
flex: 1;
display: flex;
padding: 2rem;
position: relative;
}
#sheet {
font-size: 1mm;
position: absolute;
display: grid;
background: #fff;
margin: auto;
left: 0;
right: 0;
top: 0;
bottom: 0;
max-width: calc(100% - 3rem);
max-height: calc(100% - 3rem);
/* Vertical A4 aspect ratio: */
aspect-ratio: 0.707070707070707;
overflow: hidden;
box-shadow: 0 0.3rem 2rem 0 rgba(0 0 0 / 0.3);
}
.sticker {
background: #e84f1dad;
margin: auto;
width: calc(var(--stickerWidth) * 1%);
aspect-ratio: var(--stickerRatio);
}
<main>
<section id="panel">
<p>
Enter the desired sticker dimensions:
</p>
<p id="units">
<label><input type="radio" name="unit" value="mm" checked> mm</label>
<label><input type="radio" name="unit" value="cm"> cm</label>
<label><input type="radio" name="unit" value="in"> in</label>
</p>
<p>
<label>Width <input id="width" type="text" value="60"><span class="unit">mm</span></label>
<br>
<label>Height <input id="height" type="text" value="60"><span class="unit">mm</span></label>
</p>
<p>
At these dimensions we can fit
<b id="count">48</b> stickers per sheet
</p>
<button type="button">Order here</button>
</section>
<section id="area">
<div id="sheet"></div>
</section>
</main>

相关内容

最新更新