GSAP 사례 연구
curveMode
배경 · MIT (local companion); GSAP Standard No Charge License (runtime)
갤러리 안에서는 화면을 벗어나거나 움직임을 멈추면 예제도 닫힙니다. 다시 재생할 때는 처음부터 시작해요.
저장한 HTML에서는 갤러리의 재생 설정이 적용되지 않아요.
SOURCE FILES
독립 예제 코드 읽기
사례의 구도나 문서의 원리를 바탕으로 새로 만든 예제입니다. 아래 코드를 복사해 직접 응용할 수 있습니다. 실행 HTML에는 미리보기를 위한 실행 환경이 들어 있습니다.
vendor/gsap-previews/sources/gsap-7384c5a6ea78/study-brief.json실행 안내·자료
{
"id": "gsap-7384c5a6ea78",
"sourceId": "gsap",
"title": "curveMode",
"sourceUrl": "https://demos.gsap.com/demo/curvemode",
"demoUrl": "https://codepen.io/GreenSock/pen/zxvRmrY",
"sourceStatus": "unresolved",
"sourceReason": "Public page unavailable through web browsing",
"sourceRevision": null,
"sourceLicense": null,
"originalDemoAcquired": false,
"disposition": "ready_for_independent_study",
"evidenceBasis": "catalog_linked_api",
"studyId": "gsap-7384c5a6ea78",
"sharedStudyCanonicalId": null,
"label": "Independently authored documentation study; original Pen unavailable",
"catalogEvidence": {
"localPath": "artifacts/full-import/gsap/remaining-docs/catalog-evidence.json",
"sha256": "4f2a4f9aa64456b85b905b562b60d7733629b41026787b3057cba29e8b10de69",
"bytes": 30117,
"recordId": "gsap-7384c5a6ea78"
},
"sourceInputs": [
{
"kind": "authored-study-specification",
"localPath": "artifacts/full-import/gsap/remaining-docs/study-specs.json",
"sha256": "cbb89331eda8783b5cf2f173b326b9195eeabfb223374fb60019161d9f46d4a6",
"bytes": 52279,
"recordId": "gsap-7384c5a6ea78"
},
{
"kind": "official-catalog-metadata",
"localPath": "artifacts/full-import/gsap/remaining-docs/catalog-evidence.json",
"sha256": "4f2a4f9aa64456b85b905b562b60d7733629b41026787b3057cba29e8b10de69",
"bytes": 30117,
"recordId": "gsap-7384c5a6ea78"
},
{
"kind": "official-documentation-evidence",
"localPath": "artifacts/full-import/gsap/remaining-docs/official-documentation-evidence.json",
"sha256": "bfa25f561e61c597569cf1bc0b3538e6dce4b9985a932775ceab41e665ca24a2",
"bytes": 11430,
"recordIds": [
"morph",
"tween"
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/gsap-core.js",
"sha256": "8063816e0e7c3e6c07970fb06feafb06146fd266db5d8346419176b3d496e284",
"gitBlob": "c673b9e2e0ee04832afb13cb892ae75f503e268c",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/gsap-core.js",
"ranges": [
{
"id": "direction-controls",
"startLine": 1354,
"endLine": 1391,
"rangeSha256": "8e2f36127b2fc568686d2d934ad3b74a21274c78bf1045d210e376260598819f"
},
{
"id": "tween-factories",
"startLine": 2637,
"endLine": 2657,
"rangeSha256": "23ae9d51865c9b7966bbc3b6cdbdb560fc790ff19e3b8bb524a3493fbf60c58a"
}
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/MorphSVGPlugin.js",
"sha256": "ed1a050ea542e5f7918b079f1a9d7094baa7ca2eef4a03c9700fc975f330859b",
"gitBlob": "16ca88dddb9dca68d6823e903e87685e031c6a39",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/MorphSVGPlugin.js",
"ranges": [
{
"id": "morph-curve-mode",
"startLine": 693,
"endLine": 745,
"rangeSha256": "62266909dc6cdfb31d563639247d86f7d5cf67336d146bb1843eb01950fab4c7"
}
]
}
],
"studiedPrinciple": "Compare coordinate interpolation with curveMode control-handle angle/length interpolation on the same authored path pair.",
"namedApis": [
{
"name": "gsap.to(path, {morphSVG: {shape, curveMode}})",
"purpose": "Use false and true in parallel controlled morphs.",
"libraryEvidence": [
"morph-curve-mode",
"tween-factories"
],
"documentationEvidence": [
"morph"
],
"minimumVersion": "3.14.0"
},
{
"name": "tween.pause(), tween.play(), tween.reverse()",
"purpose": "Control both comparison tweens together.",
"libraryEvidence": [
"direction-controls"
],
"documentationEvidence": [
"tween"
]
}
],
"implementationBrief": {
"scene": "Two equally sized SVG viewports containing identical copies of a newly drawn smooth cubic outline. Each morphs to the same second outline with changed tangent angles and handle lengths; only curveMode differs.",
"controls": [
{
"label": "Play comparison",
"type": "button",
"action": "Restart both comparisons from their source path with the same timing."
},
{
"label": "Progress",
"type": "range",
"action": "Pause both and set an identical normalized progress from zero to one."
},
{
"label": "Reverse",
"type": "button",
"action": "Reverse both existing comparison tweens from the current playhead."
},
{
"label": "Reset",
"type": "button",
"action": "Restore the original authored d strings and rebuild both tweens."
}
],
"mechanismSteps": [
"Define two compatible closed paths with several smooth cubic anchors and nonzero handles. Reuse exactly the same source and endpoint strings in both panels.",
"Construct paused tweens with morphSVG:{shape:endPath,curveMode:false} on the left and curveMode:true on the right; use the same duration and ease.",
"Synchronize progress and direction. When replaying, restore source d explicitly so the prior endpoint is not recaptured as a new start.",
"Explain that true changes how control handles interpolate; the comparison makes no universal better/worse claim.",
"If the chosen geometry produces little visible difference, retain that honest result or revise the independently authored path pair and record the change; do not fabricate a difference in the label."
]
},
"authoredDefaults": {
"durationSeconds": 1.6,
"ease": "none",
"initialProgress": 0,
"viewBox": "0 0 240 240",
"pathArt": "Two new closed outlines with six smooth cubic anchors"
},
"verificationRequired": [
"Both panels use identical start/end path strings and differ only in curveMode.",
"At progress 0 and 1 the supplied endpoints are recognizable; intermediate d values change and remain finite.",
"Reverse and direct progress jumps work without recreating tweens on every toggle.",
"Static view shows labeled source and destination outlines; no animation is required to understand the study."
],
"unsupportedClaims": [
"Original composition, assets, timing, breakpoint behavior, and live interaction are not observable in captured frames. This brief does not establish visual fidelity or recover original code.",
"curveMode is not guaranteed to improve every path pair; closely spaced anchors or different geometry may look worse.",
"No original path art, exact easing, or comparison controls were recovered."
],
"dependencies": {
"package": "gsap",
"version": "3.15.0",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"repository": "https://github.com/greensock/GSAP",
"plugins": [
"MorphSVGPlugin"
],
"registration": "gsap.registerPlugin(MorphSVGPlugin) after loading core and those plugin scripts",
"runtimeCandidates": [
{
"path": "dist/gsap.js",
"localPath": "artifacts/full-import/gsap/library/dist/gsap.js",
"gitBlob": "ec5ffee3f099bc454f2f1c79cee6a55ba97e7400",
"sha256": "01effe0e121c22ba0f885a3f091f3dc6d1f0d445c4fba37dcb298e314eca5cfb",
"bytes": 179557
},
{
"path": "dist/MorphSVGPlugin.js",
"localPath": "artifacts/full-import/gsap/library/dist/MorphSVGPlugin.js",
"gitBlob": "40531f708058713a7ad2942b74d0e1442c31431c",
"sha256": "786b51ffe878fa747bb7e115815d438ec9113bde1303967172c34b129f5472ef",
"bytes": 55989
}
],
"license": {
"name": "GSAP Standard 'No Charge' License (custom, not MIT)",
"localPath": "artifacts/full-import/gsap/licenses/gsap-standard-license.html",
"sha256": "bf8536e67b77db780f7db0c14a645f184f554bb19e938d1e52d58986e2f10528",
"bytes": 6988,
"url": "https://gsap.com/community/standard-license/"
},
"originalDependencyVersionKnown": false
},
"runtimeVerified": false,
"implementationWritten": false
}
vendor/gsap-previews/sources/gsap-7384c5a6ea78/source-inputs.json실행 안내·자료
[
{
"kind": "authored-study-specification",
"localPath": "artifacts/full-import/gsap/remaining-docs/study-specs.json",
"sha256": "cbb89331eda8783b5cf2f173b326b9195eeabfb223374fb60019161d9f46d4a6",
"bytes": 52279,
"recordId": "gsap-7384c5a6ea78"
},
{
"kind": "official-catalog-metadata",
"localPath": "artifacts/full-import/gsap/remaining-docs/catalog-evidence.json",
"sha256": "4f2a4f9aa64456b85b905b562b60d7733629b41026787b3057cba29e8b10de69",
"bytes": 30117,
"recordId": "gsap-7384c5a6ea78"
},
{
"kind": "official-documentation-evidence",
"localPath": "artifacts/full-import/gsap/remaining-docs/official-documentation-evidence.json",
"sha256": "bfa25f561e61c597569cf1bc0b3538e6dce4b9985a932775ceab41e665ca24a2",
"bytes": 11430,
"recordIds": [
"morph",
"tween"
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/gsap-core.js",
"sha256": "8063816e0e7c3e6c07970fb06feafb06146fd266db5d8346419176b3d496e284",
"gitBlob": "c673b9e2e0ee04832afb13cb892ae75f503e268c",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/gsap-core.js",
"ranges": [
{
"id": "direction-controls",
"startLine": 1354,
"endLine": 1391,
"rangeSha256": "8e2f36127b2fc568686d2d934ad3b74a21274c78bf1045d210e376260598819f"
},
{
"id": "tween-factories",
"startLine": 2637,
"endLine": 2657,
"rangeSha256": "23ae9d51865c9b7966bbc3b6cdbdb560fc790ff19e3b8bb524a3493fbf60c58a"
}
]
},
{
"kind": "original-library-source",
"localPath": "artifacts/full-import/gsap/library/src/MorphSVGPlugin.js",
"sha256": "ed1a050ea542e5f7918b079f1a9d7094baa7ca2eef4a03c9700fc975f330859b",
"gitBlob": "16ca88dddb9dca68d6823e903e87685e031c6a39",
"revision": "13e2b790546426a1a2e0e9b409f3f8dc6d6611f2",
"sourcePath": "src/MorphSVGPlugin.js",
"ranges": [
{
"id": "morph-curve-mode",
"startLine": 693,
"endLine": 745,
"rangeSha256": "62266909dc6cdfb31d563639247d86f7d5cf67336d146bb1843eb01950fab4c7"
}
]
}
]
함께 쓰는 파일 4개 보기
vendor/gsap-previews/sources/gsap-7384c5a6ea78/index.html실행 안내·자료
<main class="study"><p class="study-label">공식 API 기반 · 독립 예제</p><header><h2>좌표와 곡선 핸들의 보간</h2><p>시작과 도착 도형은 같고, 오른쪽만 제어 핸들의 각도·길이를 보간합니다.</p></header><div class="stage pair"><figure><figcaption>curveMode: false</figcaption><svg viewBox="0 0 240 240" role="img" aria-label="curveMode: false 곡선"><path d="M168.00 203.14 C149.25 209.63 120.50 172.83 97.50 158.97 C74.50 145.11 34.25 140.35 30.00 120.00 C25.75 99.65 53.25 43.36 72.00 36.86 C90.75 30.37 119.50 67.17 142.50 81.03 C165.50 94.89 205.75 99.65 210.00 120.00 C214.25 140.35 186.75 196.64 168.00 203.14Z" fill="none" stroke="#a0adca" stroke-dasharray="4 5" opacity=".4"/><path class="morph-path" d="M198.00 120.00 C197.17 145.11 181.67 177.74 161.00 191.01 C140.33 204.29 93.83 211.51 74.00 199.67 C54.17 187.84 41.17 145.11 42.00 120.00 C42.83 94.89 58.33 62.26 79.00 48.99 C99.67 35.71 146.17 28.49 166.00 40.33 C185.83 52.16 198.83 94.89 198.00 120.00Z" fill="#95efd2"/></svg></figure><figure><figcaption>curveMode: true</figcaption><svg viewBox="0 0 240 240" role="img" aria-label="curveMode: true 곡선"><path d="M168.00 203.14 C149.25 209.63 120.50 172.83 97.50 158.97 C74.50 145.11 34.25 140.35 30.00 120.00 C25.75 99.65 53.25 43.36 72.00 36.86 C90.75 30.37 119.50 67.17 142.50 81.03 C165.50 94.89 205.75 99.65 210.00 120.00 C214.25 140.35 186.75 196.64 168.00 203.14Z" fill="none" stroke="#a0adca" stroke-dasharray="4 5" opacity=".4"/><path class="morph-path" d="M198.00 120.00 C197.17 145.11 181.67 177.74 161.00 191.01 C140.33 204.29 93.83 211.51 74.00 199.67 C54.17 187.84 41.17 145.11 42.00 120.00 C42.83 94.89 58.33 62.26 79.00 48.99 C99.67 35.71 146.17 28.49 166.00 40.33 C185.83 52.16 198.83 94.89 198.00 120.00Z" fill="#fbadce"/></svg></figure></div><div class="controls"><button id="play" type="button">함께 재생</button><button id="reverse" type="button">역재생</button><button id="reset" type="button">초기화</button><label>진행<input id="progress" type="range" min="0" max="100" value="0" step="1"></label><label><input id="static-mode" type="checkbox">정적 보기</label></div><p class="readout" id="reading" aria-live="off"></p><p class="endpoint-note">점선은 새로 그린 도착 윤곽입니다. 어느 방식이 항상 더 좋다고 가정하지 않습니다.</p></main>vendor/gsap-previews/sources/gsap-7384c5a6ea78/style.css실행 안내·자료
*{box-sizing:border-box}body{background:#111522;color:#f5f2eb;font-family:system-ui,sans-serif;margin:0}button{font:inherit}.study{margin:0 auto;max-width:1100px;min-height:100vh;padding:clamp(20px,4vw,42px)}.study-label{color:#95efd2;font-size:12px;letter-spacing:.08em;margin:0 0 24px}header h2{font-size:clamp(24px,4vw,40px);letter-spacing:-.04em;margin:0 0 12px}header p{color:#aeb7c9;line-height:1.6;margin:0 0 24px;max-width:62ch}.controls{display:flex;flex-wrap:wrap;gap:8px;margin:20px 0}button{background:#26314b;border:1px solid #617394;border-radius:9px;color:#f5f2eb;cursor:pointer;padding:9px 14px}button:hover{background:#334261}button:focus-visible{outline:3px solid #95efd2;outline-offset:3px}button:disabled{cursor:default;opacity:.45}.stage{align-items:center;background:radial-gradient(ellipse at top,#243253,#141c2d);border:1px solid #3d4c68;border-radius:20px;display:flex;justify-content:center;min-height:220px;overflow:hidden;position:relative}.note{color:#aeb7c9;font-size:13px;line-height:1.5}.readout{color:#95efd2;font-family:ui-monospace,monospace;font-size:13px;min-height:2em}.stage svg{display:block;max-width:100%}code{font-family:ui-monospace,monospace}button[aria-pressed="true"]{background:#95efd2;color:#111522}
.study{padding:18px;max-width:960px}.study-label{margin-bottom:10px}header h2{font-size:clamp(22px,4vw,32px);margin-bottom:8px}header p{font-size:14px;margin-bottom:16px}.controls{align-items:center;gap:9px;margin:14px 0}.controls label{align-items:center;color:#cbd4e3;display:flex;flex-wrap:wrap;font-size:13px;gap:7px}.controls input[type=range]{accent-color:#95efd2;max-width:145px}.controls select{background:#26314b;border:1px solid #617394;border-radius:7px;color:#f5f2eb;font:inherit;padding:6px}.stage{min-height:180px}.readout{line-height:1.5;margin:8px 0}.small-code{background:#172036;border:1px solid #354258;border-radius:10px;color:#c3d1e7;font-size:12px;line-height:1.5;max-height:180px;overflow:auto;padding:12px;white-space:pre-wrap;word-break:break-word}.pair{display:flex;gap:12px;justify-content:center}.pair figure{flex:1;margin:0;max-width:300px;min-width:0;padding:8px}.pair figcaption{color:#aeb7c9;font-family:ui-monospace,monospace;font-size:12px;margin-bottom:6px;text-align:center}.pair svg{display:block;max-height:210px;width:100%}.tile-art{align-items:center;border-radius:14px;color:#152039;display:flex;justify-content:center}.tile-art svg{height:65%;width:65%}.menu-stage{min-height:196px;padding:18px}.menu-panels{display:flex;gap:10px;max-width:570px;width:100%}.menu-panel{align-items:center;background:linear-gradient(135deg,#95efd2,#75d8ed);border-radius:14px;color:#142137;display:flex;flex:1;flex-direction:column;font-weight:650;gap:10px;justify-content:center;min-height:135px;min-width:0;padding:12px}.menu-panel:nth-child(2){background:linear-gradient(135deg,#fbadce,#897dff)}.menu-panel:nth-child(3){background:linear-gradient(135deg,#ffd28c,#fc7f8e)}.menu-panel svg{height:58px;width:58px}.icon-bars{display:inline-flex;flex-direction:column;gap:5px;margin-right:7px;vertical-align:middle}.icon-bars span{background:currentColor;display:block;height:2px;width:17px}.endpoint-note{color:#aeb7c9;font-size:12px;line-height:1.5}button[hidden],[hidden]{display:none!important}@media(max-width:380px){.study{padding:14px}.controls label{max-width:100%}.menu-panels{gap:6px}.menu-panel{font-size:12px;padding:7px}.pair{gap:0}}
vendor/gsap-previews/sources/gsap-7384c5a6ea78/script.js실행 안내·자료
gsap.registerPlugin(MorphSVGPlugin);
const q=selector=>document.querySelector(selector),qa=selector=>Array.from(document.querySelectorAll(selector));
const studyAbort=new AbortController(),studyCleanup=[];
const listen=(target,type,handler)=>target.addEventListener(type,handler,{signal:studyAbort.signal});
const preference=matchMedia('(prefers-reduced-motion: reduce)'),staticControl=q('#static-mode');
staticControl.checked=preference.matches;staticControl.disabled=preference.matches;
const isStatic=()=>staticControl.checked||preference.matches;
let applyMode=()=>{};
listen(staticControl,'change',()=>applyMode());
listen(preference,'change',()=>{staticControl.checked=preference.matches;staticControl.disabled=preference.matches;applyMode();});
listen(window,'pagehide',()=>{studyCleanup.forEach(cleanup=>cleanup());studyAbort.abort();});
const reading=text=>q('#reading').textContent=text;
const SOURCE="M198.00 120.00 C197.17 145.11 181.67 177.74 161.00 191.01 C140.33 204.29 93.83 211.51 74.00 199.67 C54.17 187.84 41.17 145.11 42.00 120.00 C42.83 94.89 58.33 62.26 79.00 48.99 C99.67 35.71 146.17 28.49 166.00 40.33 C185.83 52.16 198.83 94.89 198.00 120.00Z",DESTINATION="M168.00 203.14 C149.25 209.63 120.50 172.83 97.50 158.97 C74.50 145.11 34.25 140.35 30.00 120.00 C25.75 99.65 53.25 43.36 72.00 36.86 C90.75 30.37 119.50 67.17 142.50 81.03 C165.50 94.89 205.75 99.65 210.00 120.00 C214.25 140.35 186.75 196.64 168.00 203.14Z",paths=qa('.morph-path');
let morphTweens=[];
const inspect=()=>{q('#progress').value=Math.round((morphTweens[0]?.progress()||0)*100);reading('진행 '+q('#progress').value+'% · 경로 문자열 길이 '+paths.map(path=>path.getAttribute('d').length).join(' / '));};
function rebuild(){morphTweens.forEach(t=>t.kill());paths.forEach(path=>path.setAttribute('d',SOURCE));
const configured={curveMode:true};
morphTweens=paths.map((path,i)=>gsap.to(path,{paused:true,duration:1.6,ease:'none',morphSVG:{shape:DESTINATION,...(i?configured:{curveMode:false})},onUpdate:()=>{if(i===0)inspect();}}));inspect();}
listen(q('#play'),'click',()=>{rebuild();morphTweens.forEach(t=>isStatic()?t.progress(1,true):t.play());inspect();});
listen(q('#reverse'),'click',()=>{morphTweens.forEach(t=>isStatic()?t.progress(0,true):t.reverse());inspect();});
listen(q('#reset'),'click',rebuild);
listen(q('#progress'),'input',e=>{morphTweens.forEach(t=>t.pause().progress(Number(e.target.value)/100,true));inspect();});
applyMode=()=>{rebuild();reading(isStatic()?'정적 보기 · 채운 시작 윤곽과 점선 도착 윤곽':'진행 0%');};
studyCleanup.push(()=>morphTweens.forEach(t=>t.kill()));applyMode();LICENSE실행 안내·자료
Locally authored companion HTML/CSS/control wiring: MIT License.
Copyright (c) 2026 StyleGallery contributors
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The typed source-inputs.json and study-brief.json preserve official catalog, documentation and original library source pointers. This is independently authored API research, not a recovered CodePen source or documentation snippet. The local MIT notice does not assign new licenses to referenced third-party sources.
GSAP 3.15.0: Copyright 2026, GreenSock. All rights reserved. Subject to https://gsap.com/standard-license. Original library notices are preserved.
BEHIND THE EXAMPLE
이 장면을 만드는 원리
같은 입력 도형에서 보간 방식만 비교
시각 차이를 설명할 때 바꾼 입력과 고정한 입력을 구분합니다.
- 이 예제에서는
- 두 패널에 같은 새 시작·도착 경로와 같은 1.6초 선형 진행을 주고 curveMode만 false·true로 다르게 지정했습니다. rebuild는 두 입력 문자열을 매번 복원하고 진행 슬라이더는 두 트윈을 같은 값으로 멈춥니다. 점선은 도착 윤곽이며 경로 문자열 길이는 관찰 값일 뿐 곡선 품질이나 성능 점수가 아닙니다.
