247 lines
11 KiB
HTML
247 lines
11 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ar" dir="rtl">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>مختبر صَقِل التفاعلي: جمع وتحليل المتجهات</title>
|
|
<style>
|
|
:root {
|
|
--bg: #06090f;
|
|
--surface: #0d111a;
|
|
--card: #131a26;
|
|
--border: rgba(255, 255, 255, 0.12);
|
|
--cyan: #00f5d4;
|
|
--amber: #ffd166;
|
|
--emerald: #10b981;
|
|
--blue: #0071e3;
|
|
--text: #f8fafc;
|
|
--text-sec: #94a3b8;
|
|
}
|
|
* { box-sizing: border-box; margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "SF Pro Arabic", sans-serif; }
|
|
body { background: var(--bg); color: var(--text); padding: 16px; display: flex; flex-direction: column; align-items: center; min-height: 100vh; }
|
|
.header { width: 100%; max-width: 800px; margin-bottom: 16px; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--border); padding-bottom: 12px; }
|
|
.header h1 { font-size: 18px; font-weight: 800; color: #fff; display: flex; align-items: center; gap: 8px; }
|
|
.badge { background: rgba(0, 245, 212, 0.15); color: var(--cyan); padding: 4px 10px; border-radius: 8px; font-size: 11px; font-weight: 700; border: 1px solid rgba(0, 245, 212, 0.3); }
|
|
|
|
.container { width: 100%; max-width: 800px; display: grid; grid-template-columns: 1fr; gap: 16px; }
|
|
@media (min-width: 768px) { .container { grid-template-columns: 1.2fr 1fr; } }
|
|
|
|
.canvas-card { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 12px; display: flex; flex-direction: column; align-items: center; position: relative; }
|
|
canvas { background: #080c14; border-radius: 12px; border: 1px solid rgba(255,255,255,0.06); width: 100%; height: auto; aspect-ratio: 1; touch-action: none; }
|
|
|
|
.controls-card { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 18px; display: flex; flex-direction: column; gap: 14px; }
|
|
.slider-group { background: var(--card); border: 1px solid var(--border); border-radius: 12px; padding: 12px; }
|
|
.slider-header { display: flex; justify-content: space-between; font-size: 13px; font-weight: 700; margin-bottom: 8px; }
|
|
.slider-header.vec-a { color: var(--cyan); }
|
|
.slider-header.vec-b { color: var(--amber); }
|
|
.slider-header.res { color: var(--emerald); }
|
|
|
|
input[type=range] { width: 100%; height: 6px; border-radius: 3px; background: rgba(255,255,255,0.1); outline: none; -webkit-appearance: none; margin-bottom: 8px; }
|
|
input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; border-radius: 50%; background: #fff; cursor: pointer; box-shadow: 0 0 8px rgba(0,0,0,0.5); }
|
|
|
|
.toggle-row { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; background: rgba(16, 185, 129, 0.1); border: 1px solid rgba(16, 185, 129, 0.3); border-radius: 12px; cursor: pointer; }
|
|
.toggle-row label { font-size: 13px; font-weight: 700; color: var(--emerald); cursor: pointer; }
|
|
|
|
.math-readout { background: #04070c; border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; padding: 12px; font-family: monospace; font-size: 12px; line-height: 1.6; color: #cbd5e1; }
|
|
.math-val { color: #fff; font-weight: bold; }
|
|
|
|
.socratic-challenge { background: rgba(255, 209, 102, 0.1); border: 1px solid rgba(255, 209, 102, 0.3); border-radius: 12px; padding: 12px; font-size: 12px; line-height: 1.5; color: #fde68a; }
|
|
.socratic-challenge strong { color: var(--amber); display: block; margin-bottom: 4px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div class="header">
|
|
<h1><span>🧪</span> مختبر جمع وتحليل المتجهات التفاعلي</h1>
|
|
<span class="badge">فيزياء 10 • الوحدة 1</span>
|
|
</div>
|
|
|
|
<div class="container">
|
|
<!-- Canvas Area -->
|
|
<div class="canvas-card">
|
|
<canvas id="vectorCanvas" width="400" height="400"></canvas>
|
|
</div>
|
|
|
|
<!-- Controls Area -->
|
|
<div class="controls-card">
|
|
<!-- Vector A Control -->
|
|
<div class="slider-group">
|
|
<div class="slider-header vec-a">
|
|
<span>المتجه A (السماوي)</span>
|
|
<span id="labelA">|A| = 100 N, θ = 30°</span>
|
|
</div>
|
|
<label style="font-size:11px;color:var(--text-sec);">المقدار (|A|):</label>
|
|
<input type="range" id="magA" min="20" max="150" value="100">
|
|
<label style="font-size:11px;color:var(--text-sec);">الزاوية (θ):</label>
|
|
<input type="range" id="angA" min="0" max="360" value="30">
|
|
</div>
|
|
|
|
<!-- Vector B Control -->
|
|
<div class="slider-group">
|
|
<div class="slider-header vec-b">
|
|
<span>المتجه B (الذهبي)</span>
|
|
<span id="labelB">|B| = 80 N, θ = 120°</span>
|
|
</div>
|
|
<label style="font-size:11px;color:var(--text-sec);">المقدار (|B|):</label>
|
|
<input type="range" id="magB" min="20" max="150" value="80">
|
|
<label style="font-size:11px;color:var(--text-sec);">الزاوية (θ):</label>
|
|
<input type="range" id="angB" min="0" max="360" value="120">
|
|
</div>
|
|
|
|
<!-- Resultant Toggle -->
|
|
<div class="toggle-row" onclick="document.getElementById('showRes').click()">
|
|
<label for="showRes">إظهار متجه المحصلة (R = A + B) 🎯</label>
|
|
<input type="checkbox" id="showRes" checked style="cursor:pointer; width:18px; height:18px; accent-color:var(--emerald);">
|
|
</div>
|
|
|
|
<!-- Real-time Mathematical Breakdown -->
|
|
<div class="math-readout" id="mathOutput">
|
|
حسابات التحليل المتعامد...
|
|
</div>
|
|
|
|
<!-- Socratic Challenge -->
|
|
<div class="socratic-challenge">
|
|
<strong>💡 فحص سقراطي مباشر:</strong>
|
|
اجعل الزاوية بين المتجهين 180° وشاهد ماذا يحدث لقيمة المحصلة R! هل تكون قيمة صغرى أم عظمى؟
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const canvas = document.getElementById('vectorCanvas');
|
|
const ctx = canvas.getContext('2d');
|
|
const magA = document.getElementById('magA');
|
|
const angA = document.getElementById('angA');
|
|
const magB = document.getElementById('magB');
|
|
const angB = document.getElementById('angB');
|
|
const showRes = document.getElementById('showRes');
|
|
const labelA = document.getElementById('labelA');
|
|
const labelB = document.getElementById('labelB');
|
|
const mathOutput = document.getElementById('mathOutput');
|
|
|
|
function drawArrow(ctx, fromx, fromy, tox, toy, color, lineWidth = 3) {
|
|
const headlen = 10;
|
|
const dx = tox - fromx;
|
|
const dy = toy - fromy;
|
|
const angle = Math.atan2(dy, dx);
|
|
ctx.strokeStyle = color;
|
|
ctx.fillStyle = color;
|
|
ctx.lineWidth = lineWidth;
|
|
ctx.beginPath();
|
|
ctx.moveTo(fromx, fromy);
|
|
ctx.lineTo(tox, toy);
|
|
ctx.stroke();
|
|
ctx.beginPath();
|
|
ctx.moveTo(tox, toy);
|
|
ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
|
|
ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
}
|
|
|
|
function render() {
|
|
const w = canvas.width;
|
|
const h = canvas.height;
|
|
const cx = w / 2;
|
|
const cy = h / 2;
|
|
|
|
ctx.clearRect(0, 0, w, h);
|
|
|
|
// Draw Grid
|
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
|
|
ctx.lineWidth = 1;
|
|
for (let x = 0; x <= w; x += 25) {
|
|
ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke();
|
|
}
|
|
for (let y = 0; y <= h; y += 25) {
|
|
ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke();
|
|
}
|
|
|
|
// Draw Axes
|
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
|
|
ctx.lineWidth = 1.5;
|
|
ctx.beginPath(); ctx.moveTo(0, cy); ctx.lineTo(w, cy); ctx.stroke();
|
|
ctx.beginPath(); ctx.moveTo(cx, 0); ctx.lineTo(cx, h); ctx.stroke();
|
|
|
|
// Math calculations
|
|
const mA = parseFloat(magA.value);
|
|
const radA = (parseFloat(angA.value) * Math.PI) / 180;
|
|
const ax = mA * Math.cos(radA);
|
|
const ay = mA * Math.sin(radA);
|
|
|
|
const mB = parseFloat(magB.value);
|
|
const radB = (parseFloat(angB.value) * Math.PI) / 180;
|
|
const bx = mB * Math.cos(radB);
|
|
const by = mB * Math.sin(radB);
|
|
|
|
const rx = ax + bx;
|
|
const ry = ay + by;
|
|
const rMag = Math.sqrt(rx * rx + ry * ry);
|
|
let rDeg = (Math.atan2(ry, rx) * 180) / Math.PI;
|
|
if (rDeg < 0) rDeg += 360;
|
|
|
|
// Update labels
|
|
labelA.textContent = `|A| = ${mA.toFixed(0)} N, θ = ${angA.value}°`;
|
|
labelB.textContent = `|B| = ${mB.toFixed(0)} N, θ = ${angB.value}°`;
|
|
|
|
// Draw Vector A (Cyan)
|
|
drawArrow(ctx, cx, cy, cx + ax, cy - ay, '#00f5d4', 3);
|
|
ctx.fillStyle = '#00f5d4';
|
|
ctx.font = 'bold 12px sans-serif';
|
|
ctx.fillText('A', cx + ax + 6, cy - ay - 4);
|
|
|
|
// Draw Vector B (Amber)
|
|
drawArrow(ctx, cx, cy, cx + bx, cy - by, '#ffd166', 3);
|
|
ctx.fillStyle = '#ffd166';
|
|
ctx.fillText('B', cx + bx + 6, cy - by - 4);
|
|
|
|
// Draw Resultant R (Emerald)
|
|
if (showRes.checked) {
|
|
drawArrow(ctx, cx, cy, cx + rx, cy - ry, '#10b981', 4);
|
|
ctx.fillStyle = '#10b981';
|
|
ctx.font = 'bold 13px sans-serif';
|
|
ctx.fillText(`R (${rMag.toFixed(1)} N)`, cx + rx + 8, cy - ry - 4);
|
|
|
|
// Draw parallelogram dotted lines
|
|
ctx.strokeStyle = 'rgba(255, 255, 255, 0.15)';
|
|
ctx.setLineDash([4, 4]);
|
|
ctx.beginPath(); ctx.moveTo(cx + ax, cy - ay); ctx.lineTo(cx + rx, cy - ry); ctx.stroke();
|
|
ctx.beginPath(); ctx.moveTo(cx + bx, cy - by); ctx.lineTo(cx + rx, cy - ry); ctx.stroke();
|
|
ctx.setLineDash([]);
|
|
}
|
|
|
|
// Draw Permanent Anti-Copy Watermark
|
|
ctx.save();
|
|
ctx.fillStyle = 'rgba(0, 245, 212, 0.18)';
|
|
ctx.font = "bold 11px -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
|
|
ctx.fillText('منصة صَقِل التعليمية الذكية © Saqel Lab - محمي وغير قابل للنقل', 12, h - 14);
|
|
ctx.restore();
|
|
|
|
// Vector Dot & Cross Products
|
|
const deltaAngle = Math.abs(aRad - bRad);
|
|
const dotProd = (mA * mB * Math.cos(deltaAngle)).toFixed(1);
|
|
const crossProd = (mA * mB * Math.sin(deltaAngle)).toFixed(1);
|
|
|
|
// Update Math readout
|
|
mathOutput.innerHTML = `
|
|
Ax = ${ax.toFixed(1)} N | Ay = ${ay.toFixed(1)} N<br>
|
|
Bx = ${bx.toFixed(1)} N | By = ${by.toFixed(1)} N<br>
|
|
<span style="color:#10b981;font-weight:bold;">Rx = Ax + Bx = ${rx.toFixed(1)} N | Ry = Ay + By = ${ry.toFixed(1)} N</span><br>
|
|
<strong>المحصلة |R| = √(${rx.toFixed(1)}² + ${ry.toFixed(1)}²) = <span class="math-val" style="color:#10b981;">${rMag.toFixed(1)} N</span></strong><br>
|
|
<strong>اتجاه المحصلة θ_R = <span class="math-val" style="color:#10b981;">${rDeg.toFixed(1)}°</span></strong><br>
|
|
<span style="color:#ffd166;">الضرب القياسي (A · B) = ${dotProd} J | الضرب المتجهي |A × B| = ${crossProd} N·m</span>
|
|
`;
|
|
}
|
|
|
|
// Disable Right-Click and Copy
|
|
document.addEventListener('contextmenu', e => e.preventDefault());
|
|
document.addEventListener('keydown', e => {
|
|
if (e.ctrlKey && (e.key === 'c' || e.key === 'u' || e.key === 's')) e.preventDefault();
|
|
});
|
|
|
|
[magA, angA, magB, angB, showRes].forEach(el => el.addEventListener('input', render));
|
|
render();
|
|
</script>
|
|
</body>
|
|
</html>
|