179 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | |
| <html>
 | |
| 
 | |
| <head>
 | |
| 	<title>Target scaler</title>
 | |
| 
 | |
| 	<style>
 | |
| 		body {
 | |
| 			max-width: 50em;
 | |
| 			margin: 1.5em auto;
 | |
| 		}
 | |
| 
 | |
| 		input {
 | |
| 			margin-left: 1em;
 | |
| 		}
 | |
| 
 | |
| 		.complex-radio {
 | |
| 			display: inline-block;
 | |
| 			text-align: center;
 | |
| 			vertical-align: top;
 | |
| 		}
 | |
| 
 | |
| 		.complex-radio img {
 | |
| 			width: 10em;
 | |
| 		}
 | |
| 
 | |
| 		.vanish {
 | |
| 			display: none;
 | |
| 		}
 | |
| 
 | |
| 		#print [id^=caption-] {
 | |
| 			text-align: center;
 | |
| 			font-family: sans-serif;
 | |
| 		}
 | |
| 
 | |
| 		#print {
 | |
| 			width: 100%;
 | |
| 		}
 | |
| 
 | |
| 		#print .target {
 | |
| 			display: block;
 | |
| 			margin-left: auto;
 | |
| 			margin-right: auto;
 | |
| 		}
 | |
| 
 | |
| 		@media screen {
 | |
| 			#print {
 | |
| 				display: none;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		@media print {
 | |
| 			#ui {
 | |
| 				display: none;
 | |
| 			}
 | |
| 		}
 | |
| 	</style>
 | |
| </head>
 | |
| 
 | |
| <body>
 | |
| 	<div id="ui">
 | |
| 		<h1>Dry Fire Target Maker</h1>
 | |
| 		<p>
 | |
| 			Scale the target by setting the simulated and actual distances to the target in any unit of length,
 | |
| 			then click <em>Print</em>.
 | |
| 		</p>
 | |
| 		<p>
 | |
| 			For example, if you want the target to appear as if it is 15m away when it is actually
 | |
| 			only 3m away, set <em>Simulated distance</em> to 15 and <em>Actual distance</em>
 | |
| 			to 3.
 | |
| 		</p>
 | |
| 		<p>
 | |
| 			The target <strong>will not</strong> fit on US letter paper if <em>Actual distance</em> is more than ~35% of
 | |
| 			<em>Simulated distance</em>.
 | |
| 		</p>
 | |
| 		<hr>
 | |
| 		<p>
 | |
| 			<label>Simulated distance<input id="simulated-range" type="number" value="40"></label>
 | |
| 		</p>
 | |
| 		<p>
 | |
| 			<label>Actual distance<input id="actual-range" type="number" value="3"></label>
 | |
| 		</p>
 | |
| 		<fieldset id="caption-settings">
 | |
| 			<legend>Caption</legend>
 | |
| 			<label><input type="radio" name="caption-pos" value="none" checked>None</label>
 | |
| 			<label><input type="radio" name="caption-pos" value="above">Above</label>
 | |
| 			<label><input type="radio" name="caption-pos" value="below">Below</label>
 | |
| 			<p><label>Text<input id="caption-input"></label></p>
 | |
| 		</fieldset>
 | |
| 		<fieldset role="radiogroup" id="target-styles">
 | |
| 			<legend>Target style</legend>
 | |
| 
 | |
| 			<label><input type="radio" name="target-style" checked>
 | |
| 				<div class="complex-radio">
 | |
| 					<div>USPSA</div><img class="target" src="uspsa.svg">
 | |
| 				</div>
 | |
| 			</label>
 | |
| 			<label><input type="radio" name="target-style">
 | |
| 				<div class="complex-radio">
 | |
| 					<div>IDPA</div><img class="target" src="idpa.svg">
 | |
| 				</div>
 | |
| 			</label>
 | |
| 			<label><input type="radio" name="target-style">
 | |
| 				<div class="complex-radio">
 | |
| 					<div>IPSC</div><img class="target" src="ipsc.svg">
 | |
| 				</div>
 | |
| 			</label>
 | |
| 		</fieldset>
 | |
| 		<p>
 | |
| 			<button id="print-button">Print</button>
 | |
| 		</p>
 | |
| 	</div>
 | |
| 
 | |
| 	<div id="print">
 | |
| 		<h1 id="caption-above"></h1>
 | |
| 		<img class="target">
 | |
| 		<h1 id="caption-below"></h1>
 | |
| 	</div>
 | |
| 
 | |
| 	<script>
 | |
| 		{
 | |
| 			let target = document.querySelector("#print .target");
 | |
| 			let simulatedRange = document.getElementById("simulated-range");
 | |
| 			let actualRange = document.getElementById("actual-range");
 | |
| 
 | |
| 			const updateScale = () => {
 | |
| 				let scale = actualRange.value / simulatedRange.value;
 | |
| 				target.style.width = target.naturalWidth * scale + "mm";
 | |
| 			};
 | |
| 			simulatedRange.addEventListener("change", updateScale);
 | |
| 			actualRange.addEventListener("change", updateScale);
 | |
| 			target.addEventListener("load", updateScale);
 | |
| 
 | |
| 			document.getElementById("print-button").addEventListener("click", print.bind(window));
 | |
| 
 | |
| 			const updateTargetStyle = () => {
 | |
| 				const checked = document.querySelector("#target-styles input[type=radio]:checked");
 | |
| 				const src = checked.labels[0].getElementsByTagName("img")[0].src;
 | |
| 				target.src = src;
 | |
| 			};
 | |
| 
 | |
| 			document.querySelectorAll("#target-styles label").forEach(label => {
 | |
| 				label.addEventListener("change", updateTargetStyle);
 | |
| 			});
 | |
| 			updateTargetStyle();
 | |
| 
 | |
| 			let captionInput = document.getElementById("caption-input");
 | |
| 			let captionAbove = document.getElementById("caption-above");
 | |
| 			let captionBelow = document.getElementById("caption-below");
 | |
| 			const updateCaptionPosition = () => {
 | |
| 				let value = document.querySelector("#caption-settings input[name=caption-pos]:checked").value;
 | |
| 				captionInput.disabled = value === "none";
 | |
| 
 | |
| 				if (value === "above") {
 | |
| 					captionAbove.classList.remove("vanish");
 | |
| 					captionBelow.classList.add("vanish");
 | |
| 				} else if (value === "below") {
 | |
| 					captionAbove.classList.add("vanish");
 | |
| 					captionBelow.classList.remove("vanish");	
 | |
| 				} else {
 | |
| 					captionAbove.classList.add("vanish");
 | |
| 					captionBelow.classList.add("vanish");
 | |
| 				}
 | |
| 			};
 | |
| 			const updateCaptionText = () => {
 | |
| 				captionAbove.innerText = captionBelow.innerText = captionInput.value;
 | |
| 			}
 | |
| 			document.querySelectorAll("#caption-settings input[name=caption-pos]").forEach(radio => {
 | |
| 				radio.addEventListener("change", updateCaptionPosition);
 | |
| 			});
 | |
| 			captionInput.addEventListener("change", updateCaptionText);
 | |
| 			captionInput.addEventListener("keydown", updateCaptionText);
 | |
| 			updateCaptionText();
 | |
| 			updateCaptionPosition();
 | |
| 		}
 | |
| 	</script>
 | |
| </body>
 | |
| 
 | |
| </html> |