Des grilles pour tout
- ZikDragon
- Dragon d'argent
- Messages : 81
- Inscription : Lun 16 Sep 2024 08:19
- Localisation :
- Version de D&D préférée : ?
- Univers de D&D préféré : ?
- Race : ?
- Classe : ?
- Alignement : ?
- Dieu :
Des grilles pour tout
Bonjour tout le monde
Etant quelque peu obsédé par les cartes et leurs échelles lorsqu'il s'agit de voyages ou de donjons, je me suis attellé à un petit code HTML qui pour le moment parvient à calmer mes crises de cartographite aiguuuuuue
donc je le partage en espérant que ça pourra servir à quelqu'un.
Voici donc un petit code qui permet de placer un calque de grille, carré ou hexagone sur pointe ou sur coté, de dimensionner la grille en pixel ou en milimetres, de charger une image en fond pour la placer sous la grille et de pouvoir ajuster aussi bien la grille que l image sur les axes X et Y ainsi que de zoomer à la molette de la souris. Une présélection A4 , A3 et format libre ( en milimetres ) sont à disposition par le menu dédié. L'impression du fichier pdf est possible pour la page construite à l'écran. J'y ai également intégré un bouton pour afficher/masquer la grille, un choix de couleur et taille de trait en pixel, et une option de coordonées.
Le tout étant bien entendu utilisable hors ligne et entièrement gratuitement , en copiant tout simplement la totalité du code ci-dessous dans un editeur de texte (genre notepad) et de le sauvegarder en utilisant l'extension .HTML
Pour l'utiliser c 'est aussi simple que démarrer un raccourci vers une page internet
Voilà , j'espère avoir pu enfin apporter une petite contribution à ce donjon déjà très très bien garni
bon jeu à toutes et tous
Etant quelque peu obsédé par les cartes et leurs échelles lorsqu'il s'agit de voyages ou de donjons, je me suis attellé à un petit code HTML qui pour le moment parvient à calmer mes crises de cartographite aiguuuuuue
Voici donc un petit code qui permet de placer un calque de grille, carré ou hexagone sur pointe ou sur coté, de dimensionner la grille en pixel ou en milimetres, de charger une image en fond pour la placer sous la grille et de pouvoir ajuster aussi bien la grille que l image sur les axes X et Y ainsi que de zoomer à la molette de la souris. Une présélection A4 , A3 et format libre ( en milimetres ) sont à disposition par le menu dédié. L'impression du fichier pdf est possible pour la page construite à l'écran. J'y ai également intégré un bouton pour afficher/masquer la grille, un choix de couleur et taille de trait en pixel, et une option de coordonées.
Le tout étant bien entendu utilisable hors ligne et entièrement gratuitement , en copiant tout simplement la totalité du code ci-dessous dans un editeur de texte (genre notepad) et de le sauvegarder en utilisant l'extension .HTML
Pour l'utiliser c 'est aussi simple que démarrer un raccourci vers une page internet
Code : Tout sélectionner
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Générateur de Grilles Pro</title>
<style>
:root {
--bg-dark: #2c3e50;
--bg-light: #34495e;
--accent: #27ae60;
--panel-bg: #ffffff;
}
body {
margin: 0;
background: var(--bg-dark);
display: flex;
flex-direction: column;
height: 100vh;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
overflow: hidden;
}
header {
background: var(--panel-bg);
padding: 8px 15px;
display: flex;
gap: 15px;
flex-wrap: wrap;
z-index: 100;
box-shadow: 0 2px 10px rgba(0,0,0,0.3);
align-items: center;
}
#viewport {
flex-grow: 1;
background: var(--bg-light);
overflow: auto;
display: flex;
justify-content: center;
align-items: flex-start;
padding: 40px;
}
.page-canvas {
background: white;
position: relative;
box-shadow: 0 0 30px rgba(0,0,0,0.5);
flex-shrink: 0;
background-repeat: no-repeat;
cursor: grab;
}
.page-canvas:active { cursor: grabbing; }
#grid-svg { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.grid-shape { fill: none; vector-effect: non-scaling-stroke; }
.grid-text { font-weight: bold; text-anchor: middle; dominant-baseline: middle; pointer-events: none; }
.hide-grid .grid-shape, .hide-coords .grid-text { display: none; }
.group { border-left: 2px solid #eee; padding-left: 10px; display: flex; gap: 8px; align-items: center; }
label { font-size: 10px; font-weight: bold; color: #7f8c8d; text-transform: uppercase; display: block; }
input[type="number"] { width: 50px; border: 1px solid #ddd; border-radius: 3px; }
.val-display { font-size: 11px; font-weight: bold; min-width: 45px; display: inline-block; color: #2c3e50; }
button { cursor: pointer; border: none; border-radius: 4px; transition: 0.2s; padding: 5px 10px; }
.btn-main { background: var(--accent); color: white; padding: 8px 15px; font-weight: bold; }
.btn-main:hover { background: #219150; }
@media print {
@page { margin: 0; }
body { background: white; }
header { display: none !important; }
#viewport { padding: 0 !important; background: white !important; overflow: visible !important; display: block !important; }
.page-canvas { box-shadow: none !important; margin: 0 !important; border: none !important; }
}
</style>
</head>
<body class="hide-coords">
<header id="controls">
<div class="group">
<div>
<label>Format Page</label>
<select id="paperFormat">
<option value="210,297">A4</option>
<option value="297,420">A3</option>
<option value="custom">Perso</option>
</select>
</div>
<div id="customDims" style="display:none">
<input type="number" id="custW" value="210">×<input type="number" id="custH" value="297">
</div>
</div>
<div class="group" style="background: #f9f9f9; padding-right: 10px; border-radius: 5px;">
<select id="gridType">
<option value="hex">Hex (Plat)</option>
<option value="hex_pointy">Hex (Pointe)</option>
<option value="sqr">Carré</option>
</select>
<div>
<label>Taille Grille</label>
<div style="display:flex; align-items:center; gap:5px;">
<input type="range" id="size" min="5" max="400" value="60">
<span class="val-display" id="sizeVal">60px</span>
<select id="unit"><option value="px">px</option><option value="mm">mm</option></select>
</div>
</div>
<div>
<label>Trait</label>
<input type="range" id="thick" min="0.5" max="10" step="0.5" value="1">
<span class="val-display" id="thickVal">1px</span>
</div>
<input type="color" id="cColor" value="#000000">
</div>
<div class="group">
<div style="font-size: 11px;">
<label>Mode Souris</label>
<input type="radio" name="mode" id="modeGrid" checked> Grille
<input type="radio" name="mode" id="modeImg"> Image
</div>
<div>
<label>Zoom Image</label>
<input type="range" id="imgZoomRange" min="5" max="500" value="100">
<span class="val-display" id="zoomVal">100%</span>
</div>
<input type="file" id="imgInp" accept="image/*" style="width: 120px; font-size: 10px;">
</div>
<div class="group">
<button onclick="document.body.classList.toggle('hide-grid')" title="Voir/Cacher">👁️</button>
<div style="text-align:center">
<label>Num.</label>
<input type="checkbox" id="showNb">
</div>
</div>
<button onclick="window.print()" class="btn-main">IMPRIMER PDF</button>
</header>
<div id="viewport">
<div class="page-canvas" id="page">
<svg id="grid-svg" xmlns="http://www.w3.org/2000/svg"></svg>
</div>
</div>
<script>
const svgNS = "http://www.w3.org/2000/svg";
const svgGrid = document.getElementById('grid-svg');
const page = document.getElementById('page');
const MM_TO_PX = 3.7795275591;
let gX = 0, gY = 0, iX = 0, iY = 0, iZoom = 100;
let isDragging = false, lastX, lastY;
let imgNaturalWidth = 0;
function updatePageSize() {
const format = document.getElementById('paperFormat').value;
const customDiv = document.getElementById('customDims');
let w, h;
if (format !== "custom") {
const dims = format.split(',');
w = dims[0]; h = dims[1];
customDiv.style.display = "none";
} else {
w = document.getElementById('custW').value;
h = document.getElementById('custH').value;
customDiv.style.display = "inline-block";
}
page.style.width = (w * MM_TO_PX) + "px";
page.style.height = (h * MM_TO_PX) + "px";
drawGrid();
}
function drawGrid() {
svgGrid.innerHTML = '';
const rawSize = parseFloat(document.getElementById('size').value);
const unit = document.getElementById('unit').value;
const type = document.getElementById('gridType').value;
const thick = document.getElementById('thick').value;
const color = document.getElementById('cColor').value;
const rect = page.getBoundingClientRect();
const size = (unit === "mm") ? rawSize * MM_TO_PX : rawSize;
document.getElementById('sizeVal').textContent = rawSize + unit;
document.getElementById('thickVal').textContent = thick + "px";
if (type === "hex") {
// Hexagone à plat
const h = size * 0.866025;
const colW = size * 0.75;
for (let c = -2; c < rect.width / colW + 2; c++) {
for (let r = -2; r < rect.height / h + 2; r++) {
let px = c * colW + (gX % (colW * 2));
let py = r * h + (gY % h);
if (c % 2 !== 0) py += h / 2;
const poly = document.createElementNS(svgNS, "polygon");
poly.setAttribute("points", `${px + size*0.25},${py} ${px + size*0.75},${py} ${px + size},${py + h*0.5} ${px + size*0.75},${py + h} ${px + size*0.25},${py + h} ${px},${py + h*0.5}`);
renderElement(poly, color, thick, c, r, px + size*0.5, py + h*0.5, size);
}
}
} else if (type === "hex_pointy") {
// Hexagone sur pointe
const w = size * 0.866025;
const rowH = size * 0.75;
for (let r = -2; r < rect.height / rowH + 2; r++) {
for (let c = -2; c < rect.width / w + 2; c++) {
let px = c * w + (gX % w);
let py = r * rowH + (gY % (rowH * 2));
if (r % 2 !== 0) px += w / 2;
const poly = document.createElementNS(svgNS, "polygon");
poly.setAttribute("points", `${px + w*0.5},${py} ${px + w},${py + size*0.25} ${px + w},${py + size*0.75} ${px + w*0.5},${py + size} ${px},${py + size*0.75} ${px},${py + size*0.25}`);
renderElement(poly, color, thick, c, r, px + w*0.5, py + size*0.5, size);
}
}
} else {
for (let c = -1; c < rect.width / size + 1; c++) {
for (let r = -1; r < rect.height / size + 1; r++) {
let px = c * size + (gX % size);
let py = r * size + (gY % size);
const rectEl = document.createElementNS(svgNS, "rect");
rectEl.setAttribute("x", px); rectEl.setAttribute("y", py);
rectEl.setAttribute("width", size); rectEl.setAttribute("height", size);
renderElement(rectEl, color, thick, c, r, px + size*0.5, py + size*0.5, size);
}
}
}
}
function renderElement(el, color, thick, c, r, tx, ty, size) {
el.setAttribute("class", "grid-shape");
el.setAttribute("stroke", color);
el.setAttribute("stroke-width", thick);
svgGrid.appendChild(el);
const text = document.createElementNS(svgNS, "text");
text.setAttribute("x", tx); text.setAttribute("y", ty);
text.setAttribute("class", "grid-text");
text.setAttribute("fill", color);
text.setAttribute("font-size", Math.max(size * 0.2, 5));
text.textContent = `${c},${r}`;
svgGrid.appendChild(text);
}
page.onmousedown = (e) => { if(e.button === 0) { isDragging = true; lastX = e.clientX; lastY = e.clientY; }};
window.onmousemove = (e) => {
if (!isDragging) return;
const dx = e.clientX - lastX, dy = e.clientY - lastY;
if (document.getElementById('modeGrid').checked) {
gX += dx; gY += dy; drawGrid();
} else {
iX += dx; iY += dy; updateImgStyle();
}
lastX = e.clientX; lastY = e.clientY;
};
window.onmouseup = () => isDragging = false;
page.onwheel = (e) => {
e.preventDefault();
if (document.getElementById('modeGrid').checked) {
let s = document.getElementById('size');
s.value = parseFloat(s.value) + (e.deltaY < 0 ? 2 : -2);
drawGrid();
} else {
iZoom += (e.deltaY < 0 ? 5 : -5);
iZoom = Math.max(5, iZoom);
document.getElementById('imgZoomRange').value = iZoom;
updateImgStyle();
}
};
function updateImgStyle() {
page.style.backgroundPosition = `${iX}px ${iY}px`;
document.getElementById('zoomVal').textContent = iZoom + "%";
if (imgNaturalWidth > 0) {
page.style.backgroundSize = `${(imgNaturalWidth * (iZoom / 100))}px auto`;
}
}
document.getElementById('paperFormat').onchange = updatePageSize;
document.getElementById('gridType').onchange = drawGrid;
document.getElementById('size').oninput = drawGrid;
document.getElementById('unit').onchange = drawGrid;
document.getElementById('thick').oninput = drawGrid;
document.getElementById('cColor').oninput = drawGrid;
document.getElementById('custW').oninput = updatePageSize;
document.getElementById('custH').oninput = updatePageSize;
document.getElementById('imgZoomRange').oninput = (e) => { iZoom = parseInt(e.target.value); updateImgStyle(); };
document.getElementById('showNb').onchange = (e) => document.body.classList.toggle('hide-coords', !e.target.checked);
document.getElementById('imgInp').onchange = (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (ev) => {
const img = new Image();
img.onload = () => {
imgNaturalWidth = img.naturalWidth;
page.style.backgroundImage = `url(${ev.target.result})`;
iX = 0; iY = 0; iZoom = 100;
document.getElementById('imgZoomRange').value = 100;
updateImgStyle();
};
img.src = ev.target.result;
};
reader.readAsDataURL(file);
};
updatePageSize();
</script>
</body>
</html>
- szass
- Staff - Façonneur de Donjons
- Messages : 12861
- Inscription : Jeu 29 Mars 2012 15:28
- Localisation : Laboratoire du Donjon
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Planescape
- Race : Githyanki
- Classe : Illusionniste
- Alignement : Chaotique Neutre
- Dieu : Vlaakith CLVII
- Mini Feuille de perso : Feuilles de personnage ► Afficher le texte
Re: Des grilles pour tout
Excellent
C'est le genre d'application très pratique (et bien foutu qui plus est) qui aurait toute sa place dans les applications du DDD si tu es d'accord.
C'est le genre d'application très pratique (et bien foutu qui plus est) qui aurait toute sa place dans les applications du DDD si tu es d'accord.
Lolth tlu malla. Jal ultrinnan zhah xundus.
- MonsieurLOlo
- Dragon d'argent
- Messages : 16
- Inscription : Mer 5 Avr 2023 14:51
- Localisation : Marseille
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Ravenloft
- Race : Elfe noir
- Classe : Assassin
- Alignement : Loyal Mauvais
- Dieu : Lloth
Re: Des grilles pour tout
je partage ça, j'avais fais un post FB mais on sait jamais ! C'est une pépite
https://www.beta.arkanatools.com/papermap/
https://www.beta.arkanatools.com/papermap/
- ZikDragon
- Dragon d'argent
- Messages : 81
- Inscription : Lun 16 Sep 2024 08:19
- Localisation :
- Version de D&D préférée : ?
- Univers de D&D préféré : ?
- Race : ?
- Classe : ?
- Alignement : ?
- Dieu :
Re: Des grilles pour tout
Bien sur que je suis d'accord, je ne savais pas trop où la placer, mais je tenais à ce qu'elle profite à qui en veut
- szass
- Staff - Façonneur de Donjons
- Messages : 12861
- Inscription : Jeu 29 Mars 2012 15:28
- Localisation : Laboratoire du Donjon
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Planescape
- Race : Githyanki
- Classe : Illusionniste
- Alignement : Chaotique Neutre
- Dieu : Vlaakith CLVII
- Mini Feuille de perso : Feuilles de personnage ► Afficher le texte
Re: Des grilles pour tout
Top, merci ZikDragon 
Je vais ajouter ça bientôt.
@MonsieurLOlo : je trouve que l'interface du générateur que tu indiques est un peu confuse et mal branlée.
Mais je n'ai peut-être pas tout compris.
- pas de pleine page (scrollbar et zoomwheel combinées ne font pas bon ménage
)
- on ne peut pas déplacer l'image
- quand on change de format (A4, A3...) la zone n'est pas agrandie en conséquence
- on ne voit pas vraiment l'effet des grilles avant l'impression (juste au survol un petit bout).
- quand on clique sur la zone, cela ouvre le modal pour imprimer le pdf
- je n'arrive pas à faire un truc propre lors de la génération du pdf
Comme je le disais, je n'ai peut-être pas tout saisi.
Mais selon moi une interface UI/UX bien faite doit être simple à prendre en main sans tutoriel, surtout quand il s'agit d'un petit programme avec peu de fonctionnalités.
Je vais ajouter ça bientôt.
@MonsieurLOlo : je trouve que l'interface du générateur que tu indiques est un peu confuse et mal branlée.
Mais je n'ai peut-être pas tout compris.
- pas de pleine page (scrollbar et zoomwheel combinées ne font pas bon ménage
- on ne peut pas déplacer l'image
- quand on change de format (A4, A3...) la zone n'est pas agrandie en conséquence
- on ne voit pas vraiment l'effet des grilles avant l'impression (juste au survol un petit bout).
- quand on clique sur la zone, cela ouvre le modal pour imprimer le pdf
- je n'arrive pas à faire un truc propre lors de la génération du pdf
Comme je le disais, je n'ai peut-être pas tout saisi.
Mais selon moi une interface UI/UX bien faite doit être simple à prendre en main sans tutoriel, surtout quand il s'agit d'un petit programme avec peu de fonctionnalités.
Lolth tlu malla. Jal ultrinnan zhah xundus.
- szass
- Staff - Façonneur de Donjons
- Messages : 12861
- Inscription : Jeu 29 Mars 2012 15:28
- Localisation : Laboratoire du Donjon
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Planescape
- Race : Githyanki
- Classe : Illusionniste
- Alignement : Chaotique Neutre
- Dieu : Vlaakith CLVII
- Mini Feuille de perso : Feuilles de personnage ► Afficher le texte
Re: Des grilles pour tout
Lolth tlu malla. Jal ultrinnan zhah xundus.
- ZikDragon
- Dragon d'argent
- Messages : 81
- Inscription : Lun 16 Sep 2024 08:19
- Localisation :
- Version de D&D préférée : ?
- Univers de D&D préféré : ?
- Race : ?
- Classe : ?
- Alignement : ?
- Dieu :
Re: Des grilles pour tout
szass a écrit : ↑Lun 25 Mai 2026 07:20C'est ajouté :
https://www.donjondudragon.fr/scripts/applications.html
![]()
- MonsieurLOlo
- Dragon d'argent
- Messages : 16
- Inscription : Mer 5 Avr 2023 14:51
- Localisation : Marseille
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Ravenloft
- Race : Elfe noir
- Classe : Assassin
- Alignement : Loyal Mauvais
- Dieu : Lloth
Re: Des grilles pour tout
Alors la video du tuto est explicite pourtant, une fois que tu choisit la taille de ton quadrillage, le soft fait tout tout seul, il découpe meme les salles du donjons, c'est vraiment orienté impression par contre il me sembleszass a écrit : ↑Lun 25 Mai 2026 06:58Top, merci ZikDragon
Je vais ajouter ça bientôt.
@MonsieurLOlo : je trouve que l'interface du générateur que tu indiques est un peu confuse et mal branlée.![]()
Mais je n'ai peut-être pas tout compris.
- pas de pleine page (scrollbar et zoomwheel combinées ne font pas bon ménage)
- on ne peut pas déplacer l'image
- quand on change de format (A4, A3...) la zone n'est pas agrandie en conséquence
- on ne voit pas vraiment l'effet des grilles avant l'impression (juste au survol un petit bout).
- quand on clique sur la zone, cela ouvre le modal pour imprimer le pdf![]()
- je n'arrive pas à faire un truc propre lors de la génération du pdf
Comme je le disais, je n'ai peut-être pas tout saisi.
Mais selon moi une interface UI/UX bien faite doit être simple à prendre en main sans tutoriel, surtout quand il s'agit d'un petit programme avec peu de fonctionnalités.
- szass
- Staff - Façonneur de Donjons
- Messages : 12861
- Inscription : Jeu 29 Mars 2012 15:28
- Localisation : Laboratoire du Donjon
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Planescape
- Race : Githyanki
- Classe : Illusionniste
- Alignement : Chaotique Neutre
- Dieu : Vlaakith CLVII
- Mini Feuille de perso : Feuilles de personnage ► Afficher le texte
Re: Des grilles pour tout
Je ne vois pas de tuto sur la page 
Lolth tlu malla. Jal ultrinnan zhah xundus.
- MonsieurLOlo
- Dragon d'argent
- Messages : 16
- Inscription : Mer 5 Avr 2023 14:51
- Localisation : Marseille
- Version de D&D préférée : AD&D2
- Univers de D&D préféré : Ravenloft
- Race : Elfe noir
- Classe : Assassin
- Alignement : Loyal Mauvais
- Dieu : Lloth


