HTML5+JS實現一個簡單的SVG 貝塞爾曲線可視化設計器,通過幾個點移動位置,控制曲線的方向

三次貝塞爾曲線,二次貝塞爾曲線有什么區別
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>貝塞爾曲線可視化設計器 &copy; 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>    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/909518.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/909518.shtml
英文地址,請注明出處:http://en.pswp.cn/news/909518.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Pytorch框架——自動微分和反向傳播

一、自動微分概念 自動微分&#xff08;Automatic Differentiation&#xff0c;AD&#xff09;是一種利用計算機程序自動計算函數導數的技術&#xff0c;它是機器學習和優化算法中的核心工具&#xff08;如神經網絡的梯度下降&#xff09;&#xff0c;通過反向傳播計算并更新梯…

【Linux手冊】進程的狀態:從創建到消亡的“生命百態”

目錄 前言 操作系統進程狀態 運行狀態 阻塞狀態 掛起狀態 Linux中具體的進程狀態 R(running)運行狀態 S(sleeping)阻塞狀態 D(disk sleep)磁盤休眠狀態 T(stopped)和t(tracing stop) X(dead)終止狀態 Z(zombie)僵尸狀態 僵尸進程的危害 前言 我們在運行可執行程序…

李沐--動手學深度學習 LSTM

1.從零開始實現LSTM #從零開始實現長短期記憶網絡 import torch from torch import nn from d2l import torch as d2l#加載時光機器數據集 batch_size,num_steps 32,35 train_iter,vocab d2l.load_data_time_machine(batch_size,num_steps)#1.定義和初始化模型參數&#xff…

面經的疑難雜癥

1.介紹一下虛擬地址&#xff0c;虛擬地址是怎么映射到物理地址的&#xff1f; 虛擬地址是指在采用虛擬存儲管理的操作系統中&#xff0c;進程訪問內存時所使用的地址。每個進程都有獨立的虛擬地址空間&#xff0c;虛擬地址通過操作系統和硬件&#xff08;如MMU&#xff0c;內存…

去噪擴散概率模型(DDPM)全解:從數學基礎到實現細節

一、 概述 在這篇博客文章中&#xff0c;我們將深入探討去噪擴散概率模型&#xff08;也被稱為 DDPMs&#xff0c;擴散模型&#xff0c;基于得分的生成模型&#xff0c;或簡稱為自動編碼器&#xff09;&#xff0c;這可以說是AIGC最近幾年飛速發展的基石&#xff0c;如果你想做…

【系統分析師】2011年真題:案例分析-答案及詳解

文章目錄 試題1【問題 1】【問題 2】【問題 3】 試題2【問題 1】【問題 2】【問題 3】 試題3【問題 1】【問題 2】【問題 3】 試題4【問題 1】【問題 2】【問題 3】 試題5【問題 1】【問題 2】【問題 3】 試題1 隨著寬帶應用快速發展&#xff0c;用戶要求系統服務提供商提供基…

【unitrix】 1.7 規范化常量類型結構(standardization.rs)

一、源碼 這段代碼實現了一個二進制數字標準化系統&#xff0c;用于將二進制數字類型&#xff08;B0/B1&#xff09;轉換為更簡潔的表示形式。 //! 二進制數字標準化模塊 / Binary Number Normalization Module //! //! 提供將二進制數字(B0/B1)標準化為更簡潔表示形式的功能…

NJet Portal 應用門戶管理介紹

nginx向云原生演進&#xff0c;All in OpenNJet&#xff01; 1. 應用門戶簡介 NJet 應用引擎是基于 Nginx 的面向互聯網和云原生應用提供的運行時組態服務程序&#xff0c;作為底層引擎&#xff0c;NJet 實現了NGINX 云原生功能增強、安全加固和代碼重構&#xff0c;利用動態加…

uni-app學習筆記三十六--分段式選項卡組件的使用

先來看效果&#xff1a; 上圖有3個選項卡&#xff08;PS:uniapp官方稱之為分段器&#xff0c;我還是習慣叫選項卡&#xff09;&#xff0c;需要實現點擊不同的選項卡時下方切換顯示對應的數據。 下面介紹下實現的過程。 1.在uniapp官方文檔下載并安裝該擴展組件&#xff1a;u…

Qt:Qt桌面程序正常退出注意事項

一般情況下&#xff0c;Qt窗體的創建和顯示命令如下&#xff1a; Main_window main_window; main_window.show(); 主窗體中設置屬性Qt::WA_DeleteOnClose setAttribute(Qt::WA_DeleteOnClose); 則在main.cpp中可以將窗體創建為指針&#xff0c;這樣在退出時可以正確釋放指針…

【arXiv2024】時間序列|TimesFM-ICF:即插即用!時間序列預測新王者!吊打微調!

論文地址&#xff1a;https://arxiv.org/pdf/2410.24087 代碼地址&#xff1a;https://github.com/uctb/TSFM 為了更好地理解時間序列模型的理論與實現&#xff0c;推薦參考UP “ThePPP時間序列” 的教學視頻。該系列內容系統介紹了時間序列相關知識&#xff0c;并提供配套的論…

從0開始學習語言模型--Day02-如何最大化利用硬件

如何利用硬件 這個單元分為內核、并行處理和推理。 內核&#xff08;Kernels&#xff09; 我們說的內核一般指的就是GPU&#xff0c;這是我們用于計算的地方&#xff0c;一般說的計算資源就指的是GPU的大小。我們模型所用的數據和參數一般存儲在內存里&#xff0c;假設把內存…

ElasticSearch配置詳解:設置內存鎖定的好處

什么是內存鎖定 "bootstrap": {"memory_lock": "true" }內存鎖定是指將Elasticsearch的JVM堆內存鎖定在物理內存中&#xff0c;防止操作系統將其交換&#xff08;swap&#xff09;到磁盤。 內存交換是操作系統的虛擬內存管理機制&#xff0c;當…

成功解決 ValueError: Unable to find resource t64.exe in package pip._vendor.distlib

解決問題 我們在本地的命令行中運行指令"python -m pip install --upgrade pip"的時候&#xff0c;報了如下的錯誤&#xff1a; 解決思路 我們需要重新安裝一下pip。 解決方法 步驟1&#xff1a; 通過執行下面的指令刪除本地的pip: python -m pip uninstall pip…

倉庫物資出入庫管理系統源碼+uniapp小程序

一款基于ThinkPHPuniapp開發的倉庫物資出入庫管理系統&#xff0c;適用于單位內部物資采購、發放管理的庫存管理系統。提供全部無加密源碼&#xff0c;支持私有化部署。 更新日志&#xff1a; 新增 基于UNIAPP開發的手機端&#xff0c;適配微信小程序 新增 字典管理 新增頁面…

基于機器學習的逐巷充填開采巖層運動地表沉降預測

基于機器學習的逐巷充填開采巖層運動地表沉降預測 1. 項目概述 本報告詳細介紹了使用Python和機器學習技術預測逐巷充填開采過程中地表沉降的方法。通過分析地質參數、開采參數和充填參數,構建預測模型評估地表沉降風險。 # 導入必要的庫 import numpy as np import pandas…

MotleyCrew ——拋棄dify、coze,手動搭建多agent工作流

1. MotleyCrew 核心組件 &#xff0d; 協調器&#xff1a; Crew MotleyCrew 的核心是一個 “Crew” 對象&#xff0c;即多代理系統的指揮者。Crew 持有一個全局的知識圖譜&#xff08;使用 Kuzu 圖數據庫&#xff09;&#xff0c;用于記錄所有任務、任務單元和其執行狀態。 Cr…

掌握這些 Python 函數,讓你的代碼更簡潔優雅

在 Python 編程世界里&#xff0c;代碼的簡潔性與可讀性至關重要。簡潔優雅的代碼不僅便于自己后期維護&#xff0c;也能讓其他開發者快速理解邏輯。而 Python 豐富的內置函數和一些實用的第三方庫函數&#xff0c;就是實現這一目標的有力武器。接下來&#xff0c;就為大家介紹…

簡說ping、telnet、netcat

簡說 ping 和 telnet 命令的作用、用法和區別&#xff0c;方便理解它們在網絡診斷中的用途。 &#x1f310; ping 命令 ? 作用&#xff1a; ping 用于檢測網絡連通性。它通過向目標主機發送 ICMP Echo 請求 并等待回應&#xff0c;從而判斷目標主機是否可達&#xff0c;并測…

基于STM32的超聲波模擬雷達設計

一、雷達概述 雷達&#xff08;Radio Detection and Ranging&#xff0c;無線電探測與測距&#xff09;是一種利用電磁波探測目標位置、速度等信息的主動式傳感器系統。其基本原理是發射電磁波并接收目標反射的回波&#xff0c;通過分析回波的時間差、頻率變化等參數&#xff0…