묻고 답해요
156만명의 커뮤니티!! 함께 토론해봐요.
인프런 TOP Writers
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
커맨드 패턴 적용
디자인 패턴을 공부하면서 실제 구현중인 서비스에 적용해보려고 노력중인데(위 이미지는 예시 코드)예시 처럼 작성했을 때의 실효성이 invoker에서 audit log 같은 공통 코드 추출하는것 이외에 잘 느껴지지 않는데, 적절하지 않은 부분에 적용하려해서 그런것일까요?-> 단축키 예시처럼 해당 커맨드를 다른곳에서'도' 사용한다면 유용할것도 같네요!!추가로 ValidateLeadFieldCommand, CreateLeadCommand 이런식으로 여러 커맨드가 순차로 실행해야하는 경우에 invoker도 커맨드마다 만들어야할까?하는 고민도 듭니다!
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
type/interface 정의 파일분리
강의 내용과는 무관하지만 평소에 고민하던 점이 있어 문의드려 봅니다 평소에 type/interface 정의를 어디 둘지 고민하는 경우가 많은데요d.ts를 만들어 타입끼리 묶어둠 각자 가장 관련도 높은 파일에 둠제로초님은 강의 예제 정도 규모의 프로젝트에서 어떻게 하시는지 궁금합니다 저는 타입이 먼게 싫어서 2번을 선호하는데 '관련도 높다'는 기준이 주관적이어서 위치를 명확히 잡기 어렵고, 개발이 진행되며 관련도가 바뀌는 경우도 생기더라고요
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
팩토리 패턴
예제에서 팩토리 메서드를 굳이 왜 써야 하는지 이해를 하지 못했습니다 심플 팩토리 예제에서 grimpanFactory라는 함수의 존재 이유가 서로 다른 생성자들을 묶어주려는 요구사항이 있기 때문으로 이해했는데요 이 요구사항에 따르면 AbstractFactory들을 만들어주더라도 결국 이들을 묶어주는 로직이 필요하고 여전히 if else가 불가피한게 아닌가 생각됩니다정리하면애초에 grimpanFactory라는 함수를 만든게 type만으로 서로 다른 클래스 인스턴스를 편리하게 생성하는게 요구사항이 있어서가 아닌지(1번이 맞다면) AbstractFactory를 만들더라도 이 요구사항을 만족하려면 어딘가엔 if else가 와야할 것 같은데 잘못 이해한 것인지(1번이 틀리다면) 묶어주는게 요구사항이 아니라면 애초에 AbstractFactory 없이 생성자 바로 호출하면 되는게 아닌지
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
강결합
강결합되는게 왜 싱글턴 패턴의 단점인지 연결이 잘 되지 않아 질문드립니다. 매개변수 주입같은 해법을 주신 것 처럼, 싱글턴 패턴과는 별개의 문제로 느껴지는데, 싱글턴을 안 하면 어떻게 나아지는지 예제 같은게 있을까요?
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
React 환경에서 클래스형 디자인 패턴 활용에 관한 질문
안녕하세요, React 환경에서 다음 강의를 보며 예제연습중인 수강생입니다. 먼저, 함수형 컴포넌트가 주류가 된 현대 React 생태계에서도 클래스형 디자인 패턴이 많이 활용되는지 궁금합니다. 특히 싱글톤, 팩토리, 빌더와 같은 클래스 기반 디자인 패턴들이 여전히 실무에서 많이 사용되고 있는지 알고 싶습니다. 또한 이러한 클래스 기반의 디자인 패턴 사용 시, React의 재렌더링 측면에서 성능 이슈가 발생하지는 않는지, 실제 개발 경험에서 어떤 장단점이 있는지 궁금합니다.
-
해결됨TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
질문 있습니다.
안녕하세요 싱글턴 강의 내용 중 매개변수로 빼거나 클래스라면 this를 사용하면 된다. 면 된다 하셨는데 this를 아래 코드처럼 운용 하는건 말하는 걸까요?? class Singleton<T> { private static instance: Singleton<any>; protected data: T; protected constructor(data: T) { this.data = data; } public static getInstance<T>(data: T): Singleton<T> { if (!this.instance) { this.instance = new this(data); } return this.instance; } public getData(): T { return this.data; } } export default Singleton; 감사합니다!!
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
[공유] State 패턴 강의 최종본 커밋에서 누락된 부분이 존재
git commit 으로 공유된 State 패턴 완성본 커밋 에서 강의 마지막 부분 (23:50) 과 달라서 해당 부분을 사용하시는 분들은 정상으로 동작하지 않을 수 있습니다.그래서 해당 공유합니다.강의 시작 부분 23:50코드 export class ChromeGrimpanMenu extends GrimpanMenu { onClickPen() { const command = new PenSelectCommand(this.grimpan); this.executeCommand(command); // { name: 'pen' }; // this.grimpan.setMode('pen'); // 변경, 해당 부분은 강의에도 나와있지는 않지만 pen 모드일때 변경이안되어서 자체적으로 추가함 this.grimpan.history.stack.push(command); } onClickEraser() { // this.executeCommand(new EraserSelectCommand(this.grimpan)); // { name: 'eraser' }; // 기존 this.grimpan.setMode('eraser'); // 변경 } onClickCircle() { // this.executeCommand(new CircleSelectCommand(this.grimpan)); // { name: 'eraser' }; this.grimpan.setMode('circle'); // 변경 } onClickRectangle() { // this.executeCommand(new RectangleSelectCommand(this.grimpan)); // { name: 'eraser' }; this.grimpan.setMode('rectangle'); // 변경 } onClickPipette() { // this.executeCommand(new PipetteSelectCommand(this.grimpan)); // { name: 'eraser' }; this.grimpan.setMode('pipette'); // 변경 }
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
팩토리 메서드에 대해서 궁금증이 생겨서 질문드려봅니다!
심플 팩토리에서 chrome, safari 등등 if문을 통해서 브라우저환경에 맞는 그림판 인스턴스를 가져올 수 있도록 한 코드가 있었는데, 팩토리 메서드가 그 역할을 대신한다고 이해했습니다. 궁금한점은 결국 크롬이든 사파리든 브라우저환경을 알아내서 main함수에 넘겨줄 수 있어야하는데 그 분기는 어디서 해야하는걸까요?function clientCode(creator: Creator) { creator.someOperation() } clientCode(new ConcreteCreator1())아래의 코드라면 new ConcreteCreator1()를 판단할 수 있는 조건 분기를 결국 어디서는 해야하지 않는가에 대한 고민입니다!
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
.
.
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
[섹션1/사전에 알아두면 좋은 TS/JS 지식]영상 재생 관련 질문 드립니다.
현재 [섹션 1/사전에 알아두면 좋은 TS/JS 지식] 영상이 검은 화면에 음성만 재생되는데 확인 부탁드립니다.감사합니다!
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
[12강] 선언식 + bind vs arrow function 어떤거 선호하시나요?
class의 메서드는 선언식으로 쓰는걸 좋아하는데bind는 쓰기 싫어서 bind 필요한것만 arrow function 쓰면 클래스 메서드가 선언식이랑 표현식 섞인게 뭔가 일관성이 없어보이더라구요.. (섞어쓰면 나중에 이거 bind 필요한 메서드였나? arrow function으로 했었나? 헷갈릴것같기도 하고?)
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
싱글톤 강의
싱글통 강의에서 숙제에 대한 답변도 볼 수있는 곳이 있을까요?
-
미해결TS/JS 디자인 패턴 with Canvas: 제로초에게 제대로 배우기
리스코프 치환원칙은 반,공변성과 같은 원리인가요?
class Parents { // 좁은 파라미터 method(name: string, test: string) { // 넓은 반환 타입 return { key: "" }; } } class Child extends Parents { // 넓은 파라미터 override method(name: string) { // 좁은 반환 타입 return { key: "", name: "" }; } } 안녕하세요,리스코프 치환원칙을 보니 반,공변성의 원칙과 같이매개변수는 반공변성을 리턴은 공변성을 가지는 것 같은데, T<child> -> T<parents> 개념이 되는 타입의 정의 원칙을 리스코프 치환 원칙이라 하는 것일까요?
-
미해결
canvas의 도형에 원하는 이미지들을 넣고 싶습니다.
const COLORS = [ "#394fb8", "#554fb8", "#605ac7", "#2a91a8", "#2e9ab2", "#32a5bf", "#81b144", "#85b944", "#8fc549", "#e0af27", "#eeba2a", "#fec72e", "#bf342d", "#ca3931", "#d7423a", ]; export class Polygon { constructor(x, y, radius, sides) { this.x = x; this.y = y; this.radius = radius; this.sides = sides; this.rotate = 0; } animate(ctx, moveX) { ctx.save(); const angle = PI2 / this.sides; const angle2 = PI2 / 4; ctx.translate(this.x, this.y); this.rotate += moveX * 0.008; ctx.rotate(this.rotate); for (let i = 0; i < this.sides; i++) { const x = this.radius * Math.cos(angle * i); const y = this.radius * Math.sin(angle * i); i == 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y); ctx.save(); ctx.fillStyle=COLORS[i] ctx.translate(x, y); ctx.rotate((((360 / this.sides) * i + 45) * Math.PI) / 180); ctx.beginPath(); for (let j = 0; j < 4; j++) { const x2 = 160 * Math.cos(angle2 * j); const y2 = 160 * Math.sin(angle2 * j); j == 0 ? ctx.moveTo(x2, y2) : ctx.lineTo(x2, y2); } ctx.fill(); ctx.closePath(); ctx.restore(); } ctx.restore(); } }지금은 COLORS 배열을 이용해 fillstyle의 색을 넣었는데, 색상 대신 특정 이미지들을 넣고싶습니다. ctx.fillstyle = colors[i] 를 어떤 식으로 바꿔야할까요..?
-
미해결애플 웹사이트 인터랙션 클론!
canvas 너비를 화면 꽉 차게 하려면 어떻게 해야 할까요?
위의 사진과 같이 2000px 혹은 그 이상의 넓은 화면에서 보면 양 옆에 보기 싫게 흰 너비가 생기게 됩니다. 이걸 어떻게 하면 없앨 수 있을까요? 아예 동영상을 렌더링 할 시 넓게 해봤더니 여전히 안되더군요.
-
해결됨애플 웹사이트 인터랙션 클론!
canvas에 사진이 출력되지 않습니다
해당 강의를 바탕으로 따로 페이지 하나를 제작하고 있는데 사진은 지정이 되지만 사진이 출력되지 않네요 ㅠ 그리고 Uncaught TypeError : Cannot read properties of undefined (reading @@@) 에러가 length랑 style에 뜨는데 원인과 해결방법을 알 수 있을까요? -> 이 부분은 해결했습니다. main.js 코드입니다 + canvas를 통해 (머그컵과 같은) video 모션 이미지들이 들어가는 곳은 section 2,3,4 입니다. (() => { // 변수 모음 let yOffset = 0; // window.pageYOffset 대신 쓸 변수 let prevScrollHeight; // 현재 스크롤 위치 이전의 섹션들의 높이 합 let currentScene = 0; // 현재 보고 있는 씬 scroll-section 몇 번째인지 let enterNewScene = false; // 새로운 세션 시작된 순간 true const sceneInfo = [ { // section 0 - 인트로 type: "sticky", heightNum: 5, // 브라우저 높이의 5배로 세팅 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-0"), messageA: document.querySelector("#scroll-section-0 .main-message.a"), messageB: document.querySelector("#scroll-section-0 .main-message.b"), messageC: document.querySelector("#scroll-section-0 .main-message.c"), messageD: document.querySelector("#scroll-section-0 .main-message.d"), }, values: { // 타이밍 구간 지정 // 나타나는 타이밍 조절 messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }], messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }], // 위치 변화 타이밍 조절 messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }], messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }], messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }], messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }], // 시라지는 타이밍 조절 messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }], messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }], messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], // 위치 조절 타이밍 조절 messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }], messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }], messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }], messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], }, }, { // section 1 - 서비스 리스트 type: "normal", // heightNum: 5, // type normal에서는 필요 없음 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-1"), content: document.querySelector("#scroll-section-1 .description"), }, }, { // section 2 - 공개행정 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-2"), // messageABCD canvas: document.querySelector("#video-canvas-2"), context: document.querySelector("#video-canvas-2").getContext("2d"), videoImages: [], }, values: { videoImageCount: 14, imageSequence: [0, 13], canvas_opacity: [1, 0, { start: 0.9, end: 1 }], canvas_opacity_out: [1, 0, { start: 0.95, end: 1 }], // ,assage opacity, translate }, }, { // section 3 - 참여행정 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-3"), // messageABCD canvas: document.querySelector("#video-canvas-3"), //content: document.querySelector("#scroll-section-3 .description"), context: document.querySelector("#video-canvas-3").getContext("2d"), videoImages: [], }, values: { videoImageCount: 15, imageSequence: [0, 14], canvas_opacity: [1, 0, { start: 0.9, end: 1 }], canvas_opacity_out: [1, 0, { start: 0.95, end: 1 }], // ,assage opacity, translate }, }, { // section 4 - 능률행정 type: "sticky", heightNum: 5, scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-4"), // messageABCD canvas: document.querySelector("#video-canvas-4"), //content: document.querySelector("#scroll-section-4 .description"), //context: document.querySelector("#video-canvas-4").getContext("2d"), videoImages: [], }, values: { videoImageCount: 12, imageSequence: [0, 11], canvas_opacity: [1, 0, { start: 0.9, end: 1 }], canvas_opacity_out: [1, 0, { start: 0.95, end: 1 }], // ,assage opacity, translate }, }, { // section 5 - 마무리 type: "sticky", heightNum: 5, // 브라우저 높이의 5배로 세팅 scrollHeight: 0, objs: { container: document.querySelector("#scroll-section-5"), messageA: document.querySelector("#scroll-section-5 .main-message.a"), messageB: document.querySelector("#scroll-section-5 .main-message.b"), messageC: document.querySelector("#scroll-section-5 .main-message.c"), messageD: document.querySelector("#scroll-section-5 .main-message.d"), }, values: { // 타이밍 구간 지정 // 나타나는 타이밍 조절 messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }], messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }], // 위치 변화 타이밍 조절 messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }], messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }], messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }], messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }], // 시라지는 타이밍 조절 messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }], messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }], messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }], // 위치 조절 타이밍 조절 messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }], messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }], messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }], messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }], }, }, ]; function setCanvasImages() { let imgElem1; for (let i = 0; i < sceneInfo[2].values.videoImageCount; i++) { imgElem1 = new Image(); imgElem1.src = `./video/section2_file/section2IMG_${i}.JPG`; sceneInfo[2].objs.videoImages.push(imgElem1); } let imgElem2; for (let i = 0; i < sceneInfo[3].values.videoImageCount; i++) { imgElem2 = new Image(); imgElem2.src = `./video/section3_file/section3IMG_${i}.JPG`; sceneInfo[3].objs.videoImages.push(imgElem2); } let imgElem3; for (let i = 0; i < sceneInfo[4].values.videoImageCount; i++) { imgElem3 = new Image(); imgElem3.src = `./video/section4_file/section4IMG_${i}.JPG`; sceneInfo[4].objs.videoImages.push(imgElem3); } } function setLayout() { // 각 스크롤 섹션의 높이 세팅 for (let i = 0; i < sceneInfo.length; i++) { if (sceneInfo[i].type === "sticky") { sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; } else if (sceneInfo[i].type === "normal") { sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight; } // 섹션의 높이를 html에 세팅 sceneInfo[ i ].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`; } yOffset = window.pageYOffset; let totalScrollHeight = 0; for (let i = 0; i < sceneInfo.length; i++) { totalScrollHeight += sceneInfo[i].scrollHeight; if (totalScrollHeight >= yOffset) { currentScene = i; break; } } document.body.setAttribute("id", `show-scene-${currentScene}`); const heightRatio = window.innerHeight / 1080; sceneInfo[2].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`; } // 값이 변화되는 걸 계산 // currentYOffset - 현재 섹션에서 얼마나 scroll되었는지 비율 계산 function calcValues(values, currentYOffset) { let rv; // return value const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / scrollHeight; // 범위에 따른 타이밍 조절 if (values.length === 3) { // start ~ end 사이 애니메이션 실행 const partScrollStart = values[2].start * scrollHeight; const partScrollEnd = values[2].end * scrollHeight; const partScrollHeight = partScrollEnd - partScrollStart; if ( currentYOffset >= partScrollStart && currentYOffset <= partScrollEnd ) { rv = ((currentYOffset - partScrollStart) / partScrollHeight) * (values[1] - values[0]) + values[0]; } else if (currentYOffset < partScrollStart) { rv = values[0]; } else if (currentYOffset > partScrollEnd) { rv = values[1]; } } else { rv = scrollRatio * (values[1] - values[0]) + values[0]; // 0~1 -> 범위 확장 return rv; } return rv; } // 애니메이션 진행 처리 함수 function playAnimation() { const objs = sceneInfo[currentScene].objs; const values = sceneInfo[currentScene].values; const currentYOffset = yOffset - prevScrollHeight; // 현재 섹션에서 위치 const scrollHeight = sceneInfo[currentScene].scrollHeight; const scrollRatio = currentYOffset / scrollHeight; //console.log(currentScene, currentYOffset); switch (currentScene) { case 0: // 인트로 // console.log('0 play'); // let sequence = Math.round(calcValues(values.imageSequence, currentYOffset)); // objs.context.drawImage(objs.videoImages[sequence], 0, 0); if (scrollRatio <= 0.22) { // in objs.messageA.style.opacity = calcValues( values.messageA_opacity_in, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues( values.messageA_opacity_out, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.42) { // in objs.messageB.style.opacity = calcValues( values.messageB_opacity_in, currentYOffset ); objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageB.style.opacity = calcValues( values.messageB_opacity_out, currentYOffset ); objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.62) { // in objs.messageC.style.opacity = calcValues( values.messageC_opacity_in, currentYOffset ); objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageC.style.opacity = calcValues( values.messageC_opacity_out, currentYOffset ); objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.82) { // in objs.messageD.style.opacity = calcValues( values.messageD_opacity_in, currentYOffset ); objs.messageD.style.transform = `translate3d(0, ${calcValues( values.messageD_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageD.style.opacity = calcValues( values.messageD_opacity_out, currentYOffset ); objs.messageD.style.transform = `translate3d(0, ${calcValues( values.messageD_translateY_out, currentYOffset )}%, 0)`; } break; case 1: // 리스트 break; case 2: // 공개행정 objs.canvas.style.opacity = calcValues( values.canvas_opacity, currentYOffset ); if (scrollRatio <= 0.5) { // in objs.canvas.style.opacity = calcValues( values.canvas_opacity_in, currentYOffset ); } else { // out objs.canvas.style.opacity = calcValues( values.canvas_opacity_out, currentYOffset ); } if (scrollRatio <= 0.25) { // in objs.messageA.style.opacity = calcValues( values.messageA_opacity_in, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues( values.messageA_opacity_out, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.57) { // in objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_in, currentYOffset )}%, 0)`; objs.messageB.style.opacity = calcValues( values.messageB_opacity_in, currentYOffset ); objs.pinB.style.transform = `scaleY(${calcValues( values.pinB_scaleY, currentYOffset )})`; } else { // out objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_out, currentYOffset )}%, 0)`; objs.messageB.style.opacity = calcValues( values.messageB_opacity_out, currentYOffset ); objs.pinB.style.transform = `scaleY(${calcValues( values.pinB_scaleY, currentYOffset )})`; } if (scrollRatio <= 0.83) { // in objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_in, currentYOffset )}%, 0)`; objs.messageC.style.opacity = calcValues( values.messageC_opacity_in, currentYOffset ); objs.pinC.style.transform = `scaleY(${calcValues( values.pinC_scaleY, currentYOffset )})`; } else { // out objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_out, currentYOffset )}%, 0)`; objs.messageC.style.opacity = calcValues( values.messageC_opacity_out, currentYOffset ); objs.pinC.style.transform = `scaleY(${calcValues( values.pinC_scaleY, currentYOffset )})`; } break; case 3: // 참여행정 objs.canvas.style.opacity = calcValues( values.canvas_opacity, currentYOffset ); if (scrollRatio <= 0.25) { // in objs.messageA.style.opacity = calcValues( values.messageA_opacity_in, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues( values.messageA_opacity_out, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.57) { // in objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_in, currentYOffset )}%, 0)`; objs.messageB.style.opacity = calcValues( values.messageB_opacity_in, currentYOffset ); objs.pinB.style.transform = `scaleY(${calcValues( values.pinB_scaleY, currentYOffset )})`; } else { // out objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_out, currentYOffset )}%, 0)`; objs.messageB.style.opacity = calcValues( values.messageB_opacity_out, currentYOffset ); objs.pinB.style.transform = `scaleY(${calcValues( values.pinB_scaleY, currentYOffset )})`; } if (scrollRatio <= 0.83) { // in objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_in, currentYOffset )}%, 0)`; objs.messageC.style.opacity = calcValues( values.messageC_opacity_in, currentYOffset ); objs.pinC.style.transform = `scaleY(${calcValues( values.pinC_scaleY, currentYOffset )})`; } else { // out objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_out, currentYOffset )}%, 0)`; objs.messageC.style.opacity = calcValues( values.messageC_opacity_out, currentYOffset ); objs.pinC.style.transform = `scaleY(${calcValues( values.pinC_scaleY, currentYOffset )})`; } break; case 4: // 능률 행정 objs.canvas.style.opacity = calcValues( values.canvas_opacity, currentYOffset ); if (scrollRatio <= 0.25) { // in objs.messageA.style.opacity = calcValues( values.messageA_opacity_in, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues( values.messageA_opacity_out, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.57) { // in objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_in, currentYOffset )}%, 0)`; objs.messageB.style.opacity = calcValues( values.messageB_opacity_in, currentYOffset ); objs.pinB.style.transform = `scaleY(${calcValues( values.pinB_scaleY, currentYOffset )})`; } else { // out objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_out, currentYOffset )}%, 0)`; objs.messageB.style.opacity = calcValues( values.messageB_opacity_out, currentYOffset ); objs.pinB.style.transform = `scaleY(${calcValues( values.pinB_scaleY, currentYOffset )})`; } if (scrollRatio <= 0.83) { // in objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_in, currentYOffset )}%, 0)`; objs.messageC.style.opacity = calcValues( values.messageC_opacity_in, currentYOffset ); objs.pinC.style.transform = `scaleY(${calcValues( values.pinC_scaleY, currentYOffset )})`; } else { // out objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_out, currentYOffset )}%, 0)`; objs.messageC.style.opacity = calcValues( values.messageC_opacity_out, currentYOffset ); objs.pinC.style.transform = `scaleY(${calcValues( values.pinC_scaleY, currentYOffset )})`; } break; case 5: // 마무리 if (scrollRatio <= 0.22) { // in objs.messageA.style.opacity = calcValues( values.messageA_opacity_in, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageA.style.opacity = calcValues( values.messageA_opacity_out, currentYOffset ); objs.messageA.style.transform = `translate3d(0, ${calcValues( values.messageA_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.42) { // in objs.messageB.style.opacity = calcValues( values.messageB_opacity_in, currentYOffset ); objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageB.style.opacity = calcValues( values.messageB_opacity_out, currentYOffset ); objs.messageB.style.transform = `translate3d(0, ${calcValues( values.messageB_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.62) { // in objs.messageC.style.opacity = calcValues( values.messageC_opacity_in, currentYOffset ); objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageC.style.opacity = calcValues( values.messageC_opacity_out, currentYOffset ); objs.messageC.style.transform = `translate3d(0, ${calcValues( values.messageC_translateY_out, currentYOffset )}%, 0)`; } if (scrollRatio <= 0.82) { // in objs.messageD.style.opacity = calcValues( values.messageD_opacity_in, currentYOffset ); objs.messageD.style.transform = `translate3d(0, ${calcValues( values.messageD_translateY_in, currentYOffset )}%, 0)`; } else { // out objs.messageD.style.opacity = calcValues( values.messageD_opacity_out, currentYOffset ); objs.messageD.style.transform = `translate3d(0, ${calcValues( values.messageD_translateY_out, currentYOffset )}%, 0)`; } break; } } // "현재 활성시킬 스크롤 씬(세션) 결정하기" // 몇 번째 스크롤 섹션 진행중인지 check // 앞파트 스크롤 섹션의 height 합으로 계산 가능 function scrollLoop() { enterNewScene = false; prevScrollHeight = 0; // 값 누적 방지용 초기화 for (let i = 0; i < currentScene; i++) { prevScrollHeight += sceneInfo[i].scrollHeight; } if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) { enterNewScene = true; currentScene++; document.body.setAttribute("id", `show-scene-${currentScene}`); // currentScene에 맞춰서 body에 하나씩 세팅된다 } if (yOffset < prevScrollHeight) { enterNewScene = true; if (currentScene == 0) return; // 브라우저 바운스 효과로 음수 에러 방지 currentScene--; document.body.setAttribute("id", `show-scene-${currentScene}`); // currentScene에 맞춰서 body에 하나씩 세팅된다 } if (enterNewScene) return; playAnimation(); //console.log(currentScene); // 몇 번째 섹션인지 } //window.addEventListener("resize", setLayout); // height에러 check window.addEventListener("scroll", () => { yOffset = window.pageYOffset; //console.log(yOffset); // 스크롤 페이지 값 scrollLoop(); }); // 새로고침 window.addEventListener("load", setLayout); window.addEventListener("resize", setLayout); setLayout(); setCanvasImages(); })();
-
미해결애플 웹사이트 인터랙션 클론!
canvas.context.transform 적용 시 다른 요소를 가리는 현상
안녕하세요! 강의 열심히 따라가고 있습니다 :) 제가 중간에 놓친 부분이 있는지, canvas.context.transform 으로 scrollHeight에 따라서 canvas 사이즈 조절 시에 일분이가 커지면서 뒤에 글씨를 가리고 있는데, 어떤 부분을 놓쳤을까요 ? 뭔가 css 일 것 같은데,css를 잘 몰라서 어떤 부분을 봐야할지모르겠습니다 ㅠㅠ ps. 사용중인 디스플레이가 지금 높이 1440px 인데요, 1080px보다 작은 화면에서는 정상적으로 표시되는것을 보아하니.. canvasScaleRatio가 1보다 클 경우에 문제가 되는 것 같아보입니다 :) transform 적용해제 (개발자도구로 제거) transform 적용후