序文滑らかな曲線生成は非常に実用的な技術です 多くの場合、いくつかのポリラインを描画し、コンピューターでそれらをスムーズに接続する必要があります。 まずは最終的な効果を見てみましょう (赤い線は入力した直線、青い線はフィッティング後の曲線です)。グラフの見栄えを良くするために、最初と最後を特別に処理することができます:) アイデアは、フィッティングにベジェ曲線を使用することです ベジェ曲線の紹介ベジェ曲線は、コンピュータ グラフィックスにおいて非常に重要なパラメトリック曲線です。 二次ベジェ曲線2次ベジェ曲線のパスは、点P0、P1、P2が与えられた関数B(t)によってトレースされます。 3次ベジェ曲線3 次曲線の場合、線形ベジェ曲線で記述される中間点 Q0、Q1、Q2 と、2 次曲線で記述される点 R0 および R1 によって構築できます。 ベジェ曲線計算機能上記の式に従って、計算関数を得ることができます 2番目の注文 /** * * * @param {数値} p0 * @param {数値} p1 * @param {数値} p2 * @param {数値} t * @戻る {*} * @memberof パス */ bezier2P(p0: 数値, p1: 数値, p2: 数値, t: 数値) { 定数 P0 = p0 * Math.pow(1 - t, 2); 定数P1 = p1 * 2 * t * (1 - t); 定数P2 = p2 * t * t; P0 + P1 + P2 を返します。 } /** * * * @param {ポイント} p0 * @param {ポイント} p1 * @param {ポイント} p2 * @param {数値} 数値 * @param {数値} ティック * @return {*} {ポイント} * @memberof パス */ getBezierNowPoint2P() 関数 p0: ポイント、 p1: ポイント、 p2: ポイント、 num: 数値、 ティック: 番号、 ): ポイント { 戻る { x: this.bezier2P(p0.x, p1.x, p2.x, num * tick), y: this.bezier2P(p0.y, p1.y, p2.y, num * tick), }; } /** * 2次ベジェ曲線の頂点データを生成する* * @param {ポイント} p0 * @param {ポイント} p1 * @param {ポイント} p2 * @param {数値} [数値=100] * @param {数値} [目盛り=1] * @戻る {*} * @memberof パス */ PBezier を作成します( p0: ポイント、 p1: ポイント、 p2: ポイント、 数値: 数値 = 100, ティック: 番号 = 1、 ){ 定数 t = tick / (num - 1); 定数ポイント = []; (i = 0; i < num; i++ とします) { ポイントを this.getBezierNowPoint2P(p0, p1, p2, i, t); ポイントをプッシュします({x: point.x, y: point.y}); } ポイントを返す。 } 第三レベル /** * 3次シール曲線の公式* * @param {数値} p0 * @param {数値} p1 * @param {数値} p2 * @param {数値} p3 * @param {数値} t * @戻る {*} * @memberof パス */ bezier3P(p0: 数値、p1: 数値、p2: 数値、p3: 数値、t: 数値) { 定数 P0 = p0 * Math.pow(1 - t, 3); 定数 P1 = 3 * p1 * t * Math.pow(1 - t, 2); 定数 P2 = 3 * p2 * Math.pow(t, 2) * (1 - t); 定数 P3 = p3 * Math.pow(t, 3); P0 + P1 + P2 + P3 を返します。 } /** * 座標を取得 * * @param {ポイント} p0 * @param {ポイント} p1 * @param {ポイント} p2 * @param {ポイント} p3 * @param {数値} 数値 * @param {数値} ティック * @戻る {*} * @memberof パス */ getBezierNowPoint3P() 関数 p0: ポイント、 p1: ポイント、 p2: ポイント、 p3: ポイント、 num: 数値、 ティック: 番号、 ){ 戻る { x: this.bezier3P(p0.x, p1.x, p2.x, p3.x, num * tick), y: this.bezier3P(p0.y, p1.y, p2.y, p3.y, num * tick), }; } /** * 3次ベジェ曲線の頂点データを生成する* * @param {Point} p0 開始点 {x: 数値、y: 数値} * @param {Point} p1 制御点1 { x : 数値、 y : 数値} * @param {Point} p2 制御点2 { x : 数値、 y : 数値} * @param {Point} p3 終点 {x: 数値、y: 数値} * @param {数値} [数値=100] * @param {数値} [目盛り=1] * @return {ポイント[]} * @memberof パス */ 3PBezierを作成します( p0: ポイント、 p1: ポイント、 p2: ポイント、 p3: ポイント、 数値: 数値 = 100, ティック: 番号 = 1、 ){ pointMum の要素を num に代入します。 _tick は、変数 _tick に代入されます。 定数 t = _tick / (pointMum - 1); 定数ポイント = []; (i = 0 とします; i < pointMum; i++) { ポイントを getBezierNowPoint3P に変換します。 ポイントをプッシュします({x: point.x, y: point.y}); } ポイントを返す。 } フィッティングアルゴリズム問題は、コントロールポイントをどうやって取得するかです。私たちは比較的簡単な方法を使用します p1-pt-p2 の角の二等分線 c1c2 を取ります。これは角の二等分線 c2 に垂直です。短辺を c1-pt c2-pt の長さとします。長さをスケールします。この長さは、おおよそ曲線の曲率として理解できます。 ここではab線分は単純に処理し、2次曲線生成のみを使用しています->🌈個人のアイディアに合わせて処理することができます bc 線分は、abc によって計算された制御点 c2 と bcd によって計算された制御点 c3 などを使用します。 /** * 滑らかな曲線を生成するために必要な制御点 * * @param {Vector2D} p1 * @param {Vector2D} pt * @param {Vector2D} p2 * @param {数値} [比率=0.3] * @戻る {*} * @memberof パス */ スムーズラインコントロールポイントを作成します( p1: ベクトル2D、 pt: ベクトル2D、 p2: ベクトル2D、 比率: 数 = 0.3、 ){ const vec1T: Vector2D = vector2dMinus(p1, pt); const vecT2: Vector2D = vector2dMinus(p1, pt); const len1: 数値 = vec1T.長さ; const len2: 数値 = vecT2.長さ; 定数v: 数値 = len1 / len2; デルタにします。 (v > 1) の場合 { デルタ = ベクトル2dマイナス( p1, vector2dPlus(pt, vector2dMinus(p2, pt).scale(1 / v))、 ); } それ以外 { デルタ = ベクトル2dマイナス( ベクトル2dプラス(pt, ベクトル2dマイナス(p1, pt).スケール(v))、 p2, ); } デルタ = delta.scale(比率); const control1: ポイント = { x: vector2dPlus(pt, delta).x, y: vector2dPlus(pt, delta).y, }; const control2: ポイント = { x: vector2dMinus(pt, delta).x, y: vector2dMinus(pt, delta).y, }; {control1, control2}を返します。 } /** * 滑らかな曲線生成 * * @param {Point[]} ポイント * @param {数値} 比率 * @戻る {*} * @memberof パス */ createSmoothLine(ポイント: Point[], 比率: 数値 = 0.3) { const len = points.length; resultPoints = [] とします。 コントロールポイントを定数で指定します。 長さ<3の場合、戻り値: (i = 0; i < len - 2; i++) の場合 { const {control1, control2} = this.createSmoothLineControlPoint( 新しい Vector2D(points[i].x, points[i].y)、 新しい Vector2D(ポイント[i + 1].x, ポイント[i + 1].y)、 新しい Vector2D(ポイント[i + 2].x, ポイント[i + 2].y)、 比率、 ); コントロールポイントをプッシュします(コントロール1)。 コントロールポイントをプッシュします(コントロール2)。 ポイント1を設定します。 ポイント2を設定します。 // 最初の制御点は1つだけ使用します if (i === 0) { points1 = this.create2PBezier(points[i], control1, points[i + 1], 50); } それ以外 { コンソールにログ出力します。 ポイント1 = this.create3PBezier( ポイント[i]、 制御ポイント[2 * i - 1]、 コントロール1、 ポイント[i + 1]、 50, ); } // 末尾部分 if (i + 2 === len - 1) { points2 = this.create2PBezier( ポイント[i + 1]、 コントロール2、 ポイント[i + 2]、 50, ); } (i + 2 === len - 1)の場合{ 結果ポイント = [...結果ポイント、...ポイント1、...ポイント2]; } それ以外 { 結果ポイント = [...結果ポイント、...ポイント1]; } } resultPoints を返します。 } サンプルコード 定数入力 = [ { x: 0, y: 0 }, { x: 150, y: 150 }, { x: 300, y: 0 }, { x: 400, y: 150 }, { x: 500, y: 0 }, { x: 650, y: 150 }, ] s = path.createSmoothLine(入力); ctx = document.getElementById('cv').getContext('2d') とします。 ctx.strokeStyle = '青'; ctx.beginPath(); ctx.moveTo(0, 0); (i = 0 とします; i < s.length; i++) { ctx.lineTo(s[i].x, s[i].y); } ctx.stroke(); ctx.beginPath(); ctx.moveTo(0, 0); (i = 0 とします; i < input.length; i++) { ctx.lineTo(入力[i].x、入力[i].y); } ctx.strokeStyle = '赤'; ctx.stroke(); document.getElementById('btn').addEventListener('click', () => { app = document.getElementById('app'); とします。 インデックスを 0 にします。 移動 = () => { インデックス < s.length の場合 { app.style.left = s[index].x - 10 + 'px'; app.style.top = s[index].y - 10 + 'px'; インデックス++; アニメーションフレームをリクエスト(移動) } } 動く() }) 付録: Vector2D関連コード/** * * * @クラス Vector2D * @extends {配列} */ クラス Vector2D は Array を拡張します { /** * Vector2D のインスタンスを作成します。 * @param {数値} [x=1] * @param {数値} [y=0] * Vector2Dの@member * */ コンストラクタ(x: 数値 = 1, y: 数値 = 0) { 素晴らしい(); this.x = x; y = y; } /** * * @param {数値} v * Vector2Dの@member */ x(v) を設定する { これ[0] = v; } /** * * @param {数値} v * Vector2Dの@member */ y(v) を設定する { これ[1] = v; } /** * * * @読み取り専用 * Vector2Dの@member */ x() を取得{ this[0]を返します。 } /** * * * @読み取り専用 * Vector2Dの@member */ y() を取得する { これ[1]を返す。 } /** * * * @読み取り専用 * Vector2Dの@member */ 長さを取得する() { Math.hypot(this.x, this.y) を返します。 } /** * * * @読み取り専用 * Vector2Dの@member */ dir() を取得する { Math.atan2(this.y, this.x) を返します。 } /** * * * @戻る {*} * Vector2Dの@member */ コピー() { 新しい Vector2D(this.x, this.y) を返します。 } /** * * * @param {*} v * @戻る {*} * Vector2Dの@member */ 追加(v) { this.x += vx; y を vy に代入する これを返します。 } /** * * * @param {*} v * @戻る {*} * Vector2Dの@member */ サブ(v) { this.x -= vx; this.y -= vy; これを返します。 } /** * * * @param {*} は * @return {Vector2D} * Vector2Dの@member */ スケール(a) { this.x * = a; y = 0; これを返します。 } /** * * * @param {*} ラジアン * @戻る {*} * Vector2Dの@member */ 回転(ラジアン) { 定数 c = Math.cos(rad); const s = Math.sin(rad); 定数[x, y] = これ; this.x = x * c + y * -s; this.y = x * s + y * c; これを返します。 } /** * * * @param {*} v * @戻る {*} * Vector2Dの@member */ クロス(v) { this.x * vy - vx * this.y を返します。 } /** * * * @param {*} v * @戻る {*} * Vector2Dの@member */ ドット(v) { this.x * vx + vy * this.y を返します。 } /** * 正規化* * @戻る {*} * Vector2Dの@member */ 正規化() { this.scale(1 / this.length) を返します。 } } /** * ベクトル加算 * * @param {*} vec1 * @param {*} vec2 * @return {Vector2D} */ 関数vector2dPlus(vec1, vec2) { 新しい Vector2D(vec1.x + vec2.x, vec1.y + vec2.y) を返します。 } /** * ベクトル減算 * * @param {*} vec1 * @param {*} vec2 * @return {Vector2D} */ 関数vector2dMinus(vec1, vec2) { 新しい Vector2D(vec1.x - vec2.x, vec1.y - vec2.y) を返します。 } エクスポート {Vector2D、vector2dPlus、vector2dMinus}; 要約するこれで、Javascript を使用して滑らかな曲線を生成する方法についての記事は終了です。滑らかな曲線を生成する JS の詳細については、123WORDPRESS.COM の以前の記事を検索するか、次の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 |
<<: DockerToolBox ファイルマウント実装コード
>>: MySQL 5.7.18 zip バージョンのインストール チュートリアル
目次1. ReactJS の紹介2. ReactJSの理解とReactJSの利点1. ReactJS...
CSS を使用するだけで、コーナーカット + ボーダー + 投影 + コンテンツの背景色のグラデーシ...
プロセス構造図Nginx はマルチプロセス構造です。マルチプロセス構造は、次のような Nginx の...
Animation.css を使うと公式サイトのフォントがだんだんと変わっていくのが分かりました。c...
目次MySQLの大文字と小文字の区別はパラメータによって制御されますMySQLの大文字と小文字の区別...
.net 開発に関しては、Microsoft の SQL Server データベースに精通しており、...
Windows システムに仮想マシンをインストールするには、 VMware Workstationソ...
textarea の形式は保存時にデータベースに保存できますが、表示時には /n と相互に変換できな...
静的ウェブサイトをホストできるサーバーは数多くあります。この記事では、nginx、apache、to...
データベースでは、UNION キーワードと UNION ALL キーワードの両方が 2 つの結果セッ...
1. オーバーフロー:非表示 オーバーフロー非表示要素に overflow:hidden が設定さ...
まず、MySQL公式サイトからインストールパッケージをダウンロードします。MySQLはオープンソース...
Deepin がルートユーザーとして Google Chrome ブラウザを起動できない問題を解決す...
MySQL の replace と replace into はどちらも頻繁に使用される関数です。r...
VueはRefを使用してレベル間でコンポーネントインスタンスを取得します例の紹介開発プロセスでは、レ...