三次貝塞爾曲線,二次貝塞爾曲線有什么區別
https://blog.csdn.net/xiaoyao961/article/details/148678265
SVG 貝塞爾曲線可視化設計器
下面是一個簡單的貝塞爾曲線可視化設計器,使用 HTML5 和 JavaScript 實現。這個設計器允許你通過拖動控制點來實時調整貝塞爾曲線的形狀。
這個貝塞爾曲線設計器具有以下特點:
直觀的交互:可以直接拖動起點、終點和控制點來調整曲線形狀
曲線類型切換:支持二次貝塞爾曲線和三次貝塞爾曲線
輔助線顯示:可以顯示或隱藏控制點之間的連接線,幫助理解曲線形成原理
顏色自定義:可以更改曲線的顏色
導出功能:可以將當前曲線導出為 SVG 文件
坐標顯示:實時顯示鼠標位置和控制點坐標
響應式設計:適配不同屏幕尺寸
您可以通過拖動各個點來觀察曲線的變化,直觀地理解貝塞爾曲線的形成原理。這對于網頁設計、動畫制作和游戲開發等領域都非常有用。
? <script src="https://cdn.tailwindcss.com"></script>3.4.16.js
?<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>貝塞爾曲線可視化設計器</title><script src="3.4.16.js"></script><link href="font-awesome.min.css" rel="stylesheet"><script>tailwind.config = {theme: {extend: {colors: {primary: '#3B82F6',secondary: '#10B981',accent: '#F59E0B',dark: '#1F2937',},fontFamily: {inter: ['Inter', 'sans-serif'],},}}}</script><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.bezier-control {cursor: move;transition: r 0.2s, fill 0.2s;}.bezier-control:hover {r: 8;fill: #F59E0B;}.bezier-handle {cursor: move;opacity: 0.5;transition: r 0.2s, fill 0.2s;}.bezier-handle:hover {r: 6;fill: #F59E0B;}.bezier-path {fill: none;stroke-width: 3;stroke-linecap: round;stroke-linejoin: round;}.bezier-guide {stroke-dasharray: 5,5;stroke-width: 1;stroke-opacity: 0.5;}}</style>
</head>
<body class="bg-gray-100 font-inter min-h-screen flex flex-col"><header class="bg-white shadow-md py-4 px-6"><div class="container mx-auto"><h1 class="text-2xl font-bold text-dark flex items-center"><i class="fa fa-curve mr-2 text-primary"></i>貝塞爾曲線可視化設計器</h1></div></header><main class="flex-1 container mx-auto p-4 flex flex-col md:flex-row gap-6"><!-- 控制面板 --><div class="md:w-1/4 bg-white rounded-lg shadow p-4"><h2 class="text-lg font-semibold mb-4 flex items-center"><i class="fa fa-sliders text-primary mr-2"></i>曲線控制</h2><div class="mb-4"><label class="block text-sm font-medium text-gray-700 mb-1">曲線類型</label><select id="curveType" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary/50"><option value="cubic">三次貝塞爾曲線</option><option value="quadratic">二次貝塞爾曲線</option></select></div><div id="cubicControls" class="mb-4"><label class="block text-sm font-medium text-gray-700 mb-1">顯示輔助線</label><div class="flex items-center space-x-4"><label class="inline-flex items-center"><input type="checkbox" id="showGuides" class="form-checkbox h-5 w-5 text-primary rounded" checked><span class="ml-2 text-sm text-gray-700">顯示控制線</span></label></div></div><div class="mb-4"><label class="block text-sm font-medium text-gray-700 mb-1">曲線顏色</label><input type="color" id="curveColor" class="w-full h-10 border border-gray-300 rounded-md" value="#3B82F6"></div><div class="mt-6"><button id="resetBtn" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2 px-4 rounded-md transition duration-200 flex items-center justify-center"><i class="fa fa-refresh mr-2"></i>重置曲線</button></div><div class="mt-4"><button id="exportBtn" class="w-full bg-secondary hover:bg-secondary/90 text-white font-medium py-2 px-4 rounded-md transition duration-200 flex items-center justify-center"><i class="fa fa-download mr-2"></i>導出SVG</button></div></div><!-- 繪圖區域 --><div class="md:w-3/4 flex-1 bg-white rounded-lg shadow overflow-hidden"><div class="p-4 border-b border-gray-200 flex justify-between items-center"><h2 class="text-lg font-semibold flex items-center"><i class="fa fa-paint-brush text-primary mr-2"></i>繪圖區域</h2><div class="text-sm text-gray-500"><span id="coordinateDisplay">坐標: -</span></div></div><div class="relative"><!-- SVG 繪圖區域 --><svg id="bezierCanvas" class="w-full h-[600px] bg-gray-50" viewBox="0 0 800 600"><!-- 網格背景 --><pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M 20 0 L 0 0 0 20" fill="none" stroke="#eee" stroke-width="0.5"/></pattern><rect width="100%" height="100%" fill="url(#grid)"/><!-- 貝塞爾曲線 --><path id="bezierPath" class="bezier-path" stroke="#3B82F6" d=""/><!-- 控制線(輔助線) --><line id="controlLine1" class="bezier-guide" stroke="#9CA3AF" x1="0" y1="0" x2="0" y2="0" /><line id="controlLine2" class="bezier-guide" stroke="#9CA3AF" x1="0" y1="0" x2="0" y2="0" /><!-- 控制點 --><circle id="startPoint" class="bezier-control" cx="100" cy="300" r="6" fill="#3B82F6" /><circle id="endPoint" class="bezier-control" cx="700" cy="300" r="6" fill="#3B82F6" /><!-- 二次貝塞爾曲線控制點 --><circle id="controlPoint1" class="bezier-handle" cx="400" cy="150" r="5" fill="#10B981" /><!-- 三次貝塞爾曲線控制點(默認隱藏) --><circle id="controlPoint2" class="bezier-handle" cx="400" cy="450" r="5" fill="#10B981"></circle></svg></div></div></main><footer class="bg-dark text-white py-4 mt-6"><div class="container mx-auto px-4 text-center text-sm"><p>貝塞爾曲線可視化設計器 © 2025</p></div></footer><script>// 初始化變量let isDragging = false;let selectedElement = null;let offset = { x: 0, y: 0 };let curveType = 'cubic';let showGuides = true;// 獲取DOM元素const svg = document.getElementById('bezierCanvas');const bezierPath = document.getElementById('bezierPath');const startPoint = document.getElementById('startPoint');const endPoint = document.getElementById('endPoint');const controlPoint1 = document.getElementById('controlPoint1');const controlPoint2 = document.getElementById('controlPoint2');const controlLine1 = document.getElementById('controlLine1');const controlLine2 = document.getElementById('controlLine2');const curveTypeSelect = document.getElementById('curveType');const showGuidesCheckbox = document.getElementById('showGuides');const curveColorInput = document.getElementById('curveColor');const resetBtn = document.getElementById('resetBtn');const exportBtn = document.getElementById('exportBtn');const coordinateDisplay = document.getElementById('coordinateDisplay');// 更新貝塞爾曲線function updateBezierCurve() {const startX = parseFloat(startPoint.getAttribute('cx'));const startY = parseFloat(startPoint.getAttribute('cy'));const endX = parseFloat(endPoint.getAttribute('cx'));const endY = parseFloat(endPoint.getAttribute('cy'));const cp1X = parseFloat(controlPoint1.getAttribute('cx'));const cp1Y = parseFloat(controlPoint1.getAttribute('cy'));const cp2X = parseFloat(controlPoint2.getAttribute('cx'));const cp2Y = parseFloat(controlPoint2.getAttribute('cy'));// 更新控制線if (showGuides) {controlLine1.setAttribute('x1', startX);controlLine1.setAttribute('y1', startY);controlLine1.setAttribute('x2', cp1X);controlLine1.setAttribute('y2', cp1Y);if (curveType === 'cubic') {controlLine2.setAttribute('x1', cp2X);controlLine2.setAttribute('y1', cp2Y);controlLine2.setAttribute('x2', endX);controlLine2.setAttribute('y2', endY);}}// 更新曲線let pathData = '';if (curveType === 'quadratic') {pathData = `M ${startX},${startY} Q ${cp1X},${cp1Y} ${endX},${endY}`;} else {pathData = `M ${startX},${startY} C ${cp1X},${cp1Y} ${cp2X},${cp2Y} ${endX},${endY}`;}bezierPath.setAttribute('d', pathData);}// 處理SVG坐標function getSVGPoint(event) {const pt = svg.createSVGPoint();pt.x = event.clientX;pt.y = event.clientY;return pt.matrixTransform(svg.getScreenCTM().inverse());}// 事件處理:開始拖動function startDrag(event) {if (event.target.classList.contains('bezier-control') || event.target.classList.contains('bezier-handle')) {isDragging = true;selectedElement = event.target;const svgPoint = getSVGPoint(event);const cx = parseFloat(selectedElement.getAttribute('cx'));const cy = parseFloat(selectedElement.getAttribute('cy'));offset.x = svgPoint.x - cx;offset.y = svgPoint.y - cy;svg.style.cursor = 'grabbing';event.preventDefault();}}// 事件處理:拖動中function drag(event) {if (isDragging) {const svgPoint = getSVGPoint(event);const cx = svgPoint.x - offset.x;const cy = svgPoint.y - offset.y;// 限制在SVG畫布內const svgRect = svg.getBoundingClientRect();const newX = Math.max(0, Math.min(cx, svgRect.width));const newY = Math.max(0, Math.min(cy, svgRect.height));selectedElement.setAttribute('cx', newX);selectedElement.setAttribute('cy', newY);// 更新坐標顯示coordinateDisplay.textContent = `坐標: (${Math.round(newX)}, ${Math.round(newY)})`;// 更新曲線updateBezierCurve();}}// 事件處理:結束拖動function endDrag() {isDragging = false;selectedElement = null;svg.style.cursor = 'default';}// 事件處理:曲線類型變更function handleCurveTypeChange() {curveType = curveTypeSelect.value;if (curveType === 'quadratic') {controlPoint2.style.display = 'none';controlLine2.style.display = 'none';} else {controlPoint2.style.display = 'block';controlLine2.style.display = 'block';}updateBezierCurve();}// 事件處理:顯示輔助線變更function handleShowGuidesChange() {showGuides = showGuidesCheckbox.checked;controlLine1.style.display = showGuides ? 'block' : 'none';controlLine2.style.display = showGuides && curveType === 'cubic' ? 'block' : 'none';}// 事件處理:曲線顏色變更function handleCurveColorChange() {bezierPath.setAttribute('stroke', curveColorInput.value);startPoint.setAttribute('fill', curveColorInput.value);endPoint.setAttribute('fill', curveColorInput.value);}// 事件處理:重置按鈕function handleReset() {// 重置點位置startPoint.setAttribute('cx', 100);startPoint.setAttribute('cy', 300);endPoint.setAttribute('cx', 700);endPoint.setAttribute('cy', 300);controlPoint1.setAttribute('cx', 400);controlPoint1.setAttribute('cy', 150);controlPoint2.setAttribute('cx', 400);controlPoint2.setAttribute('cy', 450);// 重置曲線類型和顏色curveTypeSelect.value = 'quadratic';curveType = 'quadratic';curveColorInput.value = '#3B82F6';bezierPath.setAttribute('stroke', '#3B82F6');startPoint.setAttribute('fill', '#3B82F6');endPoint.setAttribute('fill', '#3B82F6');// 更新顯示controlPoint2.style.display = 'none';controlLine2.style.display = 'none';showGuidesCheckbox.checked = true;showGuides = true;// 更新曲線updateBezierCurve();}// 事件處理:導出SVGfunction handleExport() {// 創建新的SVG元素const exportSvg = svg.cloneNode(true);// 移除事件監聽器和不必要的元素exportSvg.removeAttribute('style');exportSvg.removeAttribute('onmousedown');exportSvg.removeAttribute('onmousemove');exportSvg.removeAttribute('onmouseup');// 移除網格背景的pattern引用const rect = exportSvg.querySelector('rect');if (rect) {rect.setAttribute('fill', 'white');}// 移除坐標顯示相關元素const coordinateDisplay = exportSvg.querySelector('#coordinateDisplay');if (coordinateDisplay) {coordinateDisplay.parentNode.removeChild(coordinateDisplay);}// 創建SVG字符串const svgData = new XMLSerializer().serializeToString(exportSvg);// 創建下載鏈接const blob = new Blob([svgData], {type: 'image/svg+xml'});const url = URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = 'bezier-curve.svg';document.body.appendChild(a);a.click();setTimeout(() => {document.body.removeChild(a);URL.revokeObjectURL(url);}, 0);}// 添加事件監聽器svg.addEventListener('mousedown', startDrag);document.addEventListener('mousemove', drag);document.addEventListener('mouseup', endDrag);curveTypeSelect.addEventListener('change', handleCurveTypeChange);showGuidesCheckbox.addEventListener('change', handleShowGuidesChange);curveColorInput.addEventListener('input', handleCurveColorChange);resetBtn.addEventListener('click', handleReset);exportBtn.addEventListener('click', handleExport);// 初始化updateBezierCurve();handleShowGuidesChange();</script>
</body>
</html>