File "wall-mount-fit-tool.php"
Full path: /home/u525140468/domains/productsizer.com/public_html/tools/wall-mount-fit-tool.php
File
size: 32.24 B (32.24 KB bytes)
MIME-type: text/x-php
Charset: utf-8
Download Open Edit Advanced Editor Back
<?php include "../autoload.php"; ?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;700&display=swap');
.gradient-bg {
background: linear-gradient(135deg, #1e40af 0%, #60a5fa 100%);
}
.result-card {
background: linear-gradient(145deg, #ffffff, #f0f7ff);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
transition: transform 0.3s ease, opacity 0.3s ease;
}
.result-card.hidden {
transform: translateY(20px);
opacity: 0;
}
.result-card:not(.hidden) {
transform: translateY(0);
opacity: 1;
}
.input-container {
background: linear-gradient(145deg, #ffffff, #f9fafb);
transition: all 0.3s ease;
}
.input-container:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
}
.range-input::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #1e40af;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.range-input::-moz-range-thumb {
width: 20px;
height: 20px;
background: #1e40af;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
.btn-primary {
transition: all 0.3s ease;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
}
.visual-container {
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect fill="%23f3f4f6" width="100" height="100"/><circle cx="50" cy="50" r="30" fill="%23dbeafe" opacity="0.5"/></svg>') repeat;
border-radius: 12px;
box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.05);
}
select {
appearance: none;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24"><path fill="%234b5563" d="M7 10l5 5 5-5H7z"/></svg>');
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 12px;
}
</style>
<div class="x">
<div class="x">
<!-- Input Form -->
<div class="mb-12">
<h2 class="text-3xl font-semibold text-gray-800 mb-6">Enter Your TV/Shelf & Wall Mount Details</h2>
<p class="text-gray-500 mb-8 text-lg">Input dimensions, VESA patterns, and stud spacing to check if your wall mount fits your TV or shelf and wall properly.</p>
<form id="wallMountFitForm" class="space-y-8">
<div class="grid grid-cols-1 md:grid-cols-4 gap-6">
<div class="space-y-3">
<label for="itemType" class="block text-sm font-medium text-gray-700">Item Type</label>
<div class="relative flex items-center">
<i data-lucide="tv" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<select id="itemType" class="pl-12 pr-10 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" required>
<option value="TV" selected>TV</option>
<option value="Shelf">Shelf</option>
</select>
</div>
</div>
<div class="space-y-3">
<label for="itemWidth" class="block text-sm font-medium text-gray-700">Item Width (ft)</label>
<div class="relative flex items-center">
<i data-lucide="monitor" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="number" id="itemWidth" class="pl-12 pr-4 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" placeholder="3.5" step="0.1" min="1" max="6" value="3.5" required>
</div>
<input type="range" id="itemWidthRange" class="range-input w-full h-2 bg-gray-200 rounded-lg cursor-pointer" min="1" max="6" step="0.1" value="3.5">
<p id="itemWidthValue" class="text-sm text-gray-500">3.5 ft</p>
</div>
<div class="space-y-3">
<label for="itemHeight" class="block text-sm font-medium text-gray-700">Item Height (ft)</label>
<div class="relative flex items-center">
<i data-lucide="ruler" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="number" id="itemHeight" class="pl-12 pr-4 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" placeholder="2.0" step="0.1" min="0.5" max="4" value="2.0" required>
</div>
<input type="range" id="itemHeightRange" class="range-input w-full h-2 bg-gray-200 rounded-lg cursor-pointer" min="0.5" max="4" step="0.1" value="2.0">
<p id="itemHeightValue" class="text-sm text-gray-500">2.0 ft</p>
</div>
<div class="space-y-3">
<label for="itemWeight" class="block text-sm font-medium text-gray-700">Item Weight (lbs)</label>
<div class="relative flex items-center">
<i data-lucide="scale" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="number" id="itemWeight" class="pl-12 pr-4 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" placeholder="50" step="1" min="10" max="200" value="50" required>
</div>
<input type="range" id="itemWeightRange" class="range-input w-full h-2 bg-gray-200 rounded-lg cursor-pointer" min="10" max="200" step="1" value="50">
<p id="itemWeightValue" class="text-sm text-gray-500">50 lbs</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="space-y-3">
<label for="vesaPattern" class="block text-sm font-medium text-gray-700">VESA Pattern (mm)</label>
<div class="relative flex items-center">
<i data-lucide="grid" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<select id="vesaPattern" class="pl-12 pr-10 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" required>
<option value="200x200" selected>200x200</option>
<option value="300x300">300x300</option>
<option value="400x400">400x400</option>
<option value="600x400">600x400</option>
<option value="None">None (Shelf)</option>
</select>
</div>
</div>
<div class="space-y-3">
<label for="mountVesaMax" class="block text-sm font-medium text-gray-700">Mount VESA Max (mm)</label>
<div class="relative flex items-center">
<i data-lucide="grid" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<select id="mountVesaMax" class="pl-12 pr-10 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" required>
<option value="200x200">200x200</option>
<option value="400x400" selected>400x400</option>
<option value="600x400">600x400</option>
<option value="800x600">800x600</option>
<option value="None">None (Shelf)</option>
</select>
</div>
</div>
<div class="space-y-3">
<label for="mountWeightCapacity" class="block text-sm font-medium text-gray-700">Mount Weight Capacity (lbs)</label>
<div class="relative flex items-center">
<i data-lucide="scale" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="number" id="mountWeightCapacity" class="pl-12 pr-4 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" placeholder="100" step="1" min="20" max="300" value="100" required>
</div>
<input type="range" id="mountWeightCapacityRange" class="range-input w-full h-2 bg-gray-200 rounded-lg cursor-pointer" min="20" max="300" step="1" value="100">
<p id="mountWeightCapacityValue" class="text-sm text-gray-500">100 lbs</p>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-3">
<label for="studSpacing" class="block text-sm font-medium text-gray-700">Wall Stud Spacing (in)</label>
<div class="relative flex items-center">
<i data-lucide="wall" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<select id="studSpacing" class="pl-12 pr-10 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" required>
<option value="16" selected>16 (Standard)</option>
<option value="24">24</option>
<option value="12">12</option>
<option value="Custom">Custom</option>
</select>
</div>
</div>
<div class="space-y-3 hidden" id="customStudSpacingContainer">
<label for="customStudSpacing" class="block text-sm font-medium text-gray-700">Custom Stud Spacing (in)</label>
<div class="relative flex items-center">
<i data-lucide="ruler" class="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400"></i>
<input type="number" id="customStudSpacing" class="pl-12 pr-4 py-3 block w-full rounded-lg border-gray-300 shadow-sm focus:border-blue-600 focus:ring focus:ring-blue-600 focus:ring-opacity-50 text-lg" placeholder="18" step="0.1" min="8" max="48" value="18">
</div>
<input type="range" id="customStudSpacingRange" class="range-input w-full h-2 bg-gray-200 rounded-lg cursor-pointer" min="8" max="48" step="0.1" value="18">
<p id="customStudSpacingValue" class="text-sm text-gray-500">18 in</p>
</div>
</div>
<button type="submit" class="btn-primary w-full bg-blue-600 text-white py-4 px-8 rounded-xl hover:bg-blue-700 flex items-center justify-center text-lg">
<i data-lucide="search" class="mr-3 h-5 w-5"></i>
Check Wall Mount Fit
</button>
</form>
</div>
<!-- Result Section -->
<div id="resultSection" class="result-card rounded-2xl p-10 hidden">
<h2 class="text-3xl font-semibold text-gray-800 mb-6 flex items-center">
<i data-lucide="check-circle-2" class="mr-3 text-green-500 h-7 w-7"></i>
Your Wall Mount Fit Assessment
</h2>
<div id="resultOutput" class="text-gray-700 space-y-6 text-base">
<!-- Results will be dynamically inserted here -->
</div>
<div class="mt-10 flex flex-col md:flex-row justify-between items-center gap-6">
<div class="flex items-center text-blue-600">
<i data-lucide="info" class="mr-2 h-5 w-5"></i>
<p class="text-sm font-medium">Based on standard VESA patterns, wall mount specifications, and stud spacing guidelines.</p>
</div>
<button id="downloadPdf" class="btn-primary bg-green-600 text-white py-3 px-8 rounded-xl hover:bg-green-700 flex items-center text-base">
<i data-lucide="download" class="mr-2 h-5 w-5"></i>
Download PDF Report
</button>
</div>
<!-- Visual Representation -->
<div class="mt-10 visual-container p-8">
<h3 class="text-xl font-medium text-gray-800 mb-4 flex items-center">
<i data-lucide="monitor" class="mr-3 text-blue-500 h-6 w-6"></i>
Interactive Wall Mount & TV/Shelf Visualization
</h3>
<div class="relative w-full h-96 bg-gray-200 rounded-xl overflow-hidden shadow-lg">
<canvas id="wallMountCanvas" class="w-full h-full"></canvas>
</div>
<p class="text-sm text-gray-600 mt-4">Visualization showing the TV/shelf, wall mount, and stud spacing alignment.</p>
</div>
<!-- Expert Insights -->
<div class="mt-10 bg-blue-50 p-8 rounded-xl shadow-sm">
<h3 class="text-xl font-medium text-gray-800 mb-4 flex items-center">
<i data-lucide="lightbulb" class="mr-3 text-yellow-500 h-6 w-6"></i>
Expert Insights for Wall Mount Installation
</h3>
<ul class="list-none space-y-4 text-gray-700 text-sm">
<li class="flex items-start">
<i data-lucide="check" class="mr-2 text-green-500 h-5 w-5"></i>
<span><strong>VESA Compatibility:</strong> Ensure the mount supports your TV/shelf VESA pattern.</span>
</li>
<li class="flex items-start">
<i data-lucide="check" class="mr-2 text-green-500 h-5 w-5"></i>
<span><strong>Weight Capacity:</strong> Mount should support at least 1.5x the item weight for safety.</span>
</li>
<li class="flex items-start">
<i data-lucide="check" class="mr-2 text-green-500 h-5 w-5"></i>
<span><strong>Stud Alignment:</strong> Secure the mount to studs (16" or 24" spacing common).</span>
</li>
<li class="flex items-start">
<i data-lucide="check" class="mr-2 text-green-500 h-5 w-5"></i>
<span><strong>Wall Type:</strong> Use appropriate anchors for drywall or masonry.</span>
</li>
<li class="flex items-start">
<i data-lucide="check" class="mr-2 text-green-500 h-5 w-5"></i>
<span><strong>Leveling:</strong> Use a level to ensure proper installation.</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<script>
// Initialize Lucid Icons
lucide.createIcons();
// Sync range and number inputs
function syncInputs(numberId, rangeId, valueId, unit) {
const numberInput = document.getElementById(numberId);
const rangeInput = document.getElementById(rangeId);
const valueDisplay = document.getElementById(valueId);
numberInput.addEventListener('input', () => {
rangeInput.value = numberInput.value;
valueDisplay.textContent = `${parseFloat(numberInput.value).toFixed(unit === 'in' ? 1 : 0)} ${unit}`;
});
rangeInput.addEventListener('input', () => {
numberInput.value = rangeInput.value;
valueDisplay.textContent = `${parseFloat(rangeInput.value).toFixed(unit === 'in' ? 1 : 0)} ${unit}`;
});
}
syncInputs('itemWidth', 'itemWidthRange', 'itemWidthValue', 'ft');
syncInputs('itemHeight', 'itemHeightRange', 'itemHeightValue', 'ft');
syncInputs('itemWeight', 'itemWeightRange', 'itemWeightValue', 'lbs');
syncInputs('mountWeightCapacity', 'mountWeightCapacityRange', 'mountWeightCapacityValue', 'lbs');
syncInputs('customStudSpacing', 'customStudSpacingRange', 'customStudSpacingValue', 'in');
// Toggle custom stud spacing input
document.getElementById('studSpacing').addEventListener('change', function () {
const customStudSpacingContainer = document.getElementById('customStudSpacingContainer');
customStudSpacingContainer.classList.toggle('hidden', this.value !== 'Custom');
});
// Form Submission
document.getElementById('wallMountFitForm').addEventListener('submit', function (e) {
e.preventDefault();
calculateWallMountFit();
});
function calculateWallMountFit() {
const itemType = document.getElementById('itemType').value;
const itemWidth = parseFloat(document.getElementById('itemWidth').value);
const itemHeight = parseFloat(document.getElementById('itemHeight').value);
const itemWeight = parseFloat(document.getElementById('itemWeight').value);
const vesaPattern = document.getElementById('vesaPattern').value;
const mountVesaMax = document.getElementById('mountVesaMax').value;
const mountWeightCapacity = parseFloat(document.getElementById('mountWeightCapacity').value);
const studSpacing = document.getElementById('studSpacing').value;
const customStudSpacing = studSpacing === 'Custom' ? parseFloat(document.getElementById('customStudSpacing').value) : null;
if (!itemType || !itemWidth || !itemHeight || !itemWeight || !vesaPattern || !mountVesaMax || !mountWeightCapacity || !studSpacing ||
itemWidth < 1 || itemHeight < 0.5 || itemWeight < 10 || mountWeightCapacity < 20 ||
(studSpacing === 'Custom' && (!customStudSpacing || customStudSpacing < 8 || customStudSpacing > 48))) {
alert('Please enter valid values (Item Width ≥ 1 ft, Height ≥ 0.5 ft, Weight ≥ 10 lbs, Mount Weight Capacity ≥ 20 lbs, Custom Stud Spacing 8-48 in).');
return;
}
// Parse VESA patterns
const parseVesa = (vesa) => vesa === 'None' ? { width: 0, height: 0 } : { width: parseInt(vesa.split('x')[0]), height: parseInt(vesa.split('x')[1]) };
const itemVesa = parseVesa(vesaPattern);
const mountVesa = parseVesa(mountVesaMax);
// Check VESA compatibility
const vesaFit = vesaPattern === 'None' && mountVesaMax === 'None' ||
(vesaPattern !== 'None' && mountVesaMax !== 'None' && itemVesa.width <= mountVesa.width && itemVesa.height <= mountVesa.height);
// Check weight capacity (1.5x safety margin)
const weightFit = itemWeight <= (mountWeightCapacity / 1.5);
// Check stud spacing (mount width assumed to cover at least one stud spacing)
const effectiveStudSpacing = studSpacing === 'Custom' ? customStudSpacing : parseFloat(studSpacing);
const mountWidthIn = mountVesaMax === 'None' ? itemWidth * 12 : Math.max(itemWidth * 12, mountVesa.width / 25.4 * 12);
const studFit = mountWidthIn >= effectiveStudSpacing;
const overallFit = vesaFit && weightFit && studFit ? 'Yes' : 'No';
// Generate output
const output = `
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 bg-white p-6 rounded-xl shadow-sm">
<div>
<p class="font-medium text-gray-700">Item Type</p>
<p class="text-xl text-blue-600 font-semibold">${itemType}</p>
</div>
<div>
<p class="font-medium text-gray-700">${itemType} Dimensions</p>
<p class="text-lg">${itemWidth.toFixed(1)} x ${itemHeight.toFixed(1)} ft, ${itemWeight} lbs</p>
</div>
<div>
<p class="font-medium text-gray-700">VESA Pattern</p>
<p class="text-lg">${vesaPattern === 'None' ? 'None' : vesaPattern} mm</p>
</div>
<div>
<p class="font-medium text-gray-700">Mount Specifications</p>
<p class="text-lg">VESA Max: ${mountVesaMax === 'None' ? 'None' : mountVesaMax} mm, Capacity: ${mountWeightCapacity} lbs</p>
</div>
<div>
<p class="font-medium text-gray-700">Stud Spacing</p>
<p class="text-lg">${effectiveStudSpacing.toFixed(1)} in</p>
</div>
<div>
<p class="font-medium text-gray-700">Fit Assessment</p>
<p class="text-lg ${overallFit === 'Yes' ? 'text-green-600' : 'text-red-600'} font-semibold">
${overallFit === 'Yes' ? 'Perfect Fit' : 'May Not Fit Properly'}
</p>
<p class="text-sm text-gray-600">${
overallFit === 'Yes'
? `The wall mount is compatible with the ${itemType.toLowerCase()}, supports its weight, and aligns with the stud spacing.`
: `Issues: ${
!vesaFit ? `Incompatible VESA pattern (need ≤${vesaPattern}, mount supports ≤${mountVesaMax}). ` : ''
}${
!weightFit ? `Insufficient weight capacity (need <${(itemWeight * 1.5).toFixed(0)} lbs, have ${mountWeightCapacity} lbs). ` : ''
}${
!studFit ? `Mount width too small for stud spacing (need ≥${effectiveStudSpacing.toFixed(1)} in, have ${mountWidthIn.toFixed(1)} in).` : ''
}`
}</p>
</div>
</div>
<div class="bg-blue-50 p-4 rounded-lg">
<p class="text-sm"><span class="font-medium">Tip:</span> Always secure the mount to wall studs and verify VESA compatibility with your ${itemType.toLowerCase()} manual.</p>
</div>
`;
document.getElementById('resultOutput').innerHTML = output;
document.getElementById('resultSection').classList.remove('hidden');
// Draw visualization
drawVisualization(itemType, itemWidth, itemHeight, vesaPattern, mountVesaMax, effectiveStudSpacing);
// Setup PDF download
setupPdfDownload(itemType, itemWidth, itemHeight, itemWeight, vesaPattern, mountVesaMax, mountWeightCapacity, effectiveStudSpacing, overallFit, vesaFit, weightFit, studFit);
}
function drawVisualization(itemType, itemWidth, itemHeight, vesaPattern, mountVesaMax, studSpacing) {
const canvas = document.getElementById('wallMountCanvas');
const ctx = canvas.getContext('2d');
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Scale factors
const scale = canvas.width / (itemWidth * 1.5 * 12); // Convert ft to in, scale to fit
const itemW = itemWidth * 12 * scale;
const itemH = itemHeight * 12 * scale;
const mountW = Math.max(itemW * 0.6, (mountVesaMax === 'None' ? itemW : parseInt(mountVesaMax.split('x')[0]) / 25.4 * scale));
const studS = studSpacing * scale;
// Draw wall background
const gradient = ctx.createLinearGradient(0, 0, 0, canvas.height);
gradient.addColorStop(0, '#e5e7eb');
gradient.addColorStop(1, '#d1d5db');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw studs
ctx.fillStyle = '#8b5c2e';
const studX = canvas.width / 2 - studS;
ctx.fillRect(xX, canvas.height * 0.2, 2 * scale, canvas.height * 0.6);
ctx.fillRect(xX + studS * 2, canvas.height * 0.2, 2 * scale, canvas.height * 0.6);
// Draw wall mount
ctx.shadowColor = 'rgba(0, 0, 0, 0.4)';
ctx.shadowBlur = 10;
ctx.shadowOffsetX = 5;
ctx.shadowOffsetY = 5;
ctx.fillStyle = '#4b5563';
const mountX = canvas.width / 2 - mountW / 2;
const mountY = canvas.height / 2 - mountW * 0.3;
ctx.fillRect(mountX, mountY, mountW, mountW * 0.6);
// Draw VESA holes on mount
if (mountVesaMax !== 'None') {
ctx.fillStyle = '#1e40af';
const vesaW = parseInt(mountVesaMax.split('x')[0]) / 25.4 * scale;
const vesaH = parseInt(mountVesaMax.split('x')[1]) / 25.4 * scale;
ctx.beginPath();
ctx.arc(mountX + mountW / 2 - vesaW / 2, mountY + mountW * 0.3 - vesaH / 2, 3, 0, 2 * Math.PI);
ctx.arc(mountX + mountW / 2 + vesaW / 2, mountY + mountW * 0.3 - vesaH / 2, 3, 0, 2 * Math.PI);
ctx.arc(mountX + mountW / 2 - vesaW / 2, mountY + mountW * 0.3 + vesaH / 2, 3, 0, 2 * Math.PI);
ctx.arc(mountX + mountW / 2 + vesaW / 2, mountY + mountW * 0.3 + vesaH / 2, 3, 0, 2 * Math.PI);
ctx.fill();
}
// Draw TV or shelf
ctx.fillStyle = itemType === 'TV' ? '#0284c7' : '#6b7280';
const itemX = canvas.width / 2 - itemW / 2;
const itemY = canvas.height / 2 - itemH / 2;
ctx.fillRect(itemX, itemY, itemW, itemH);
// Draw VESA holes on item
if (vesaPattern !== 'None') {
ctx.fillStyle = '#ffffff';
const vesaW = parseInt(vesaPattern.split('x')[0]) / 25.4 * scale;
const vesaH = parseInt(vesaPattern.split('x')[1]) / 25.4 * scale;
ctx.beginPath();
ctx.arc(itemX + itemW / 2 - vesaW / 2, itemY + itemH / 2 - vesaH / 2, 2, 0, 2 * Math.PI);
ctx.arc(itemX + itemW / 2 + vesaW / 2, itemY + itemH / 2 - vesaH / 2, 2, 0, 2 * Math.PI);
ctx.arc(itemX + itemW / 2 - vesaW / 2, itemY + itemH / 2 + vesaH / 2, 2, 0, 2 * Math.PI);
ctx.arc(itemX + itemW / 2 + vesaW / 2, itemY + itemH / 2 + vesaH / 2, 2, 0, 2 * Math.PI);
ctx.fill();
}
// Draw text
ctx.shadowColor = 'transparent';
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 18px Poppins';
ctx.textAlign = 'center';
ctx.fillText(`${itemType} (${itemWidth.toFixed(1)} x ${itemHeight.toFixed(1)} ft)`, itemX + itemW / 2, itemY + itemH / 2);
ctx.fillStyle = '#1e40af';
ctx.fillText(`Mount VESA: ${mountVesaMax}`, canvas.width / 2, mountY - 10);
ctx.font = '14px Poppins';
ctx.fillStyle = '#4b5563';
ctx.fillText(`Stud Spacing: ${studSpacing.toFixed(1)} in`, canvas.width / 2, canvas.height - 30);
}
function setupPdfDownload(itemType, itemWidth, itemHeight, itemWeight, vesaPattern, mountVesaMax, mountWeightCapacity, studSpacing, overallFit, vesaFit, weightFit, studFit) {
document.getElementById('downloadPdf').onclick = function () {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
// Header
doc.setFont('helvetica', 'bold');
doc.setFontSize(20);
doc.setTextColor(31, 64, 175);
doc.text(`HomeFit Tools - Wall Mount Fit Assessment for ${itemType}`, 20, 20);
doc.setFont('helvetica', 'normal');
doc.setFontSize(10);
doc.setTextColor(100, 100, 100);
doc.text(`Generated on: ${new Date().toLocaleString()}`, 20, 28);
// Results
doc.setFontSize(14);
doc.setTextColor(0, 0, 0);
doc.text(`Your Wall Mount ${itemType} Fit Assessment`, 20, 40);
doc.setFontSize(11);
doc.text(`Item Type: ${itemType}`, 20, 50);
doc.text(`${itemType} Dimensions: ${itemWidth.toFixed(1)} x ${itemHeight.toFixed(1)} ft, ${itemWeight} lbs`, 20, 58);
doc.text(`VESA Pattern: ${vesaPattern === 'None' ? 'None' : vesaPattern} mm`, 20, 66);
doc.text(`Mount Specifications: VESA Max: ${mountVesaMax === 'None' ? 'None' : mountVesaMax} mm, Capacity: ${mountWeightCapacity} lbs`, 20, 74);
doc.text(`Stud Spacing: ${studSpacing.toFixed(1)} in`, 20, 82);
doc.text(`Fit Assessment: ${overallFit === 'Yes' ? 'Perfect Fit' : 'May Not Fit Properly'}`, 20, 90);
doc.text(`Recommendation: ${
overallFit === 'Yes'
? `The wall mount is suitable for the ${itemType.toLowerCase()} and wall stud spacing.`
: `Consider a different mount or stud configuration.`
}`, 20, 98);
// Expert Insights
doc.setFont('helvetica', 'bold');
doc.setFontSize(12);
doc.text('Expert Insights', 20, 110);
doc.setFont('helvetica', 'normal');
doc.setFontSize(10);
doc.text('• VESA Compatibility: Ensure the mount supports your VESA pattern.', 20, 118);
doc.text('• Weight Capacity: Mount should support at least 1.5x the item weight.', 20, 126);
doc.text('• Stud Alignment: Secure the mount to studs (16" or 24" spacing common).', 20, 134);
doc.text('• Wall Type: Use appropriate anchors for drywall or masonry.', 20, 142);
doc.text('• Leveling: Use a level to ensure for proper installation.', 20, 150);
doc.save(`HomeFit_Wall_Mount_${itemType}_Fit_Assessment.pdf`);
doc.save(`HomeFit_Wall_Mount_${itemType}_Fit_Assessment.pdf`);
};
}
</script>