Заметки про канвас

Рома Ахмадуллин

Заметки про канвас

Рома Ахмадуллин

Рома Ахмадуллин

Задача

Сверстать промо-баннер

Доке 4 года!

Доке 4 года!

Доке 4 года!

🤔 Чего-то не хватает...

🎉 Нужен праздник!

💡Канвас

Доке 4 года!

Доке 4 года!

💅🏻 Стилёво!

Что можно делать
с помощью канваса?

😍 Красиво

А полезные вещи можно делать?

Канвас – тема!

Но...

Задачи на канвас бывают редко

Особенности канваса забываются

📝 Заметки про канвас

#1 Задание размера

            
                div {
                    width: 300px;
                    height: 150px;
                }
            
        
            
                div {
                    width: 300px;
                    height: 150px;
                }


                canvas {
                    width: 300px;
                    height: 150px;
                }
            
        
            
                div {
                    width: 300px;
                    height: 150px;
                }


                canvas {
                    width: 300px;
                    height: 150px;
                }
            
        
            
                div {
                    width: 300px;
                    height: 150px;
                }


                canvas {
                    width: 300px;
                    height: 150px;
                }
            
        
            
                div {
                    width: 300px;
                    height: 150px;
                }


                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="300"
                    height="150"
                >
            
        
            
                div {
                    width: 300px;
                    height: 150px;
                }


                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
            
                div {
                    width: 300px;
                    height: 150px;
                }


                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        

Почему так работает?

            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        

Канвас работает с пикселями

            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
            
                canvas {
                    width: 300px;
                    height: 150px;
                }

                <canvas 
                    width="600" 
                    height="300"
                >
            
        
        
            const width = 300;
            const height = 150;

            const scaleIndex = window.devicePixelRatio;

            canvas.width = width * scaleIndex;
            canvas.height = height * scaleIndex;

            canvas.style.width = `${width}px`;
            canvas.style.height = `${height}px`;
        
        

            const width = 300;
            const height = 150;

            const scaleIndex = window.devicePixelRatio;

            canvas.width = width * scaleIndex;
            canvas.height = height * scaleIndex;

            canvas.style.width = `${width}px`;
            canvas.style.height = `${height}px`;
        
        

            const width = 300;
            const height = 150;

            const scaleIndex = window.devicePixelRatio;

            canvas.width = width * scaleIndex;
            canvas.height = height * scaleIndex;

            canvas.style.width = `${width}px`;
            canvas.style.height = `${height}px`;
        
        

            const width = 300;
            const height = 150;

            const scaleIndex = window.devicePixelRatio;

            canvas.width = width * scaleIndex;
            canvas.height = height * scaleIndex;

            canvas.style.width = `${width}px`;
            canvas.style.height = `${height}px`;
        
        

            const width = 300;
            const height = 150;

            const scaleIndex = window.devicePixelRatio;

            canvas.width = width * scaleIndex;
            canvas.height = height * scaleIndex;

            canvas.style.width = `${width}px`;
            canvas.style.height = `${height}px`;
        
        

            const width = 300;
            const height = 150;

            const scaleIndex = window.devicePixelRatio;

            canvas.width = width * scaleIndex;
            canvas.height = height * scaleIndex;

            canvas.style.width = `${width}px`;
            canvas.style.height = `${height}px`;
        
        

#2 Обновление размера холста

Почему картинка не тянется?

🕵🏻‍♂️ Нужно следить
за размером канваса

            
                const setCanvasSize = () => {
                    const canvasWidth = window.innerWidth;
                    const canvasHeight = window.innerHeight;
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                window.addEventListener('resize', setCanvasSize);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = window.innerWidth;
                    const canvasHeight = window.innerHeight;
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                window.addEventListener('resize', setCanvasSize);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = window.innerWidth;
                    const canvasHeight = window.innerHeight;
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                window.addEventListener('resize', setCanvasSize);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = window.innerWidth;
                    const canvasHeight = window.innerHeight;
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                window.addEventListener('resize', setCanvasSize);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = getCanvasWidth();
                    const canvasHeight = getCanvasHeight();
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                const resizeObserver = new ResizeObserver(setCanvasSize);
        
                resizeObserver.observe(canvasWrapper);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = getCanvasWidth();
                    const canvasHeight = getCanvasHeight();
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                const resizeObserver = new ResizeObserver(setCanvasSize);
        
                resizeObserver.observe(canvasWrapper);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = getCanvasWidth();
                    const canvasHeight = getCanvasHeight();
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                const resizeObserver = new ResizeObserver(setCanvasSize);
        
                resizeObserver.observe(canvasWrapper);
            
        
            
                const setCanvasSize = () => {
                    const canvasWidth = getCanvasWidth();
                    const canvasHeight = getCanvasHeight();
        
                    canvas.width = canvasWidth * devicePixelRatio;
                    canvas.height = canvasHeight * devicePixelRatio;
        
                    canvas.style.setProperty('width', `${canvasWidth}px`);
                    canvas.style.setProperty('height', `${canvasHeight}px`);
                };

                const resizeObserver = new ResizeObserver(setCanvasSize);
        
                resizeObserver.observe(canvasWrapper);
            
        

#3 Масштабирование контента

        
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        
        
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        
        
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        
        
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        
        
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.font = "bold 60px sans-serif";
        ctx2.fillText('Abc', 240, 180);
        
        
        
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.font = "bold 60px sans-serif";
        ctx2.fillText('Abc', 240, 180);
        
        

Неудобно

Можно иначе?

        
        ctx1.scale(1, 1);
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        
        
        ctx2.scale(2, 2);
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        

        ctx1.scale(1, 1);
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        

        ctx2.scale(2, 2);
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        

        ctx1.scale(1, 1);
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        

        ctx2.scale(2, 2);
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        

        ctx1.scale(1, 1);
        ctx1.font = "bold 30px sans-serif";
        ctx1.fillText('Abc', 120, 90);
        
        

        ctx2.scale(2, 2);
        ctx2.font = "bold 30px sans-serif";
        ctx2.fillText('Abc', 120, 90);
        
        
        
            const ctx = canvas.getContext('2d');

            const scaleIndex = window.devicePixelRatio;

            ctx.scale(scaleIndex, scaleIndex);

            ctx.font = "bold 30px sans-serif";
            ctx.fillText('my-text', 135, 90);
        
        
        
            const ctx = canvas.getContext('2d');

            const scaleIndex = window.devicePixelRatio;

            ctx.scale(scaleIndex, scaleIndex);

            ctx.font = "bold 30px sans-serif";
            ctx.fillText('my-text', 135, 90);
        
        
        
            const ctx = canvas.getContext('2d');

            const scaleIndex = window.devicePixelRatio;

            ctx.scale(scaleIndex, scaleIndex);

            ctx.font = "bold 30px sans-serif";
            ctx.fillText('my-text', 135, 90);
        
        
        
            const ctx = canvas.getContext('2d');

            const scaleIndex = window.devicePixelRatio;

            ctx.scale(scaleIndex, scaleIndex);

            ctx.font = "bold 30px sans-serif";
            ctx.fillText('my-text', 135, 90);
        
        

#4 Скорость анимации

        
            const animate = () => {
                // код с анимацией
                ballPosition = ballPosition + 10;
            };

            const render = () => {
                animate();

                requestAnimationFrame(render);
            };

            render();
        
        
            
            const animate = () => {
                // код с анимацией
                ballPosition = ballPosition + 10;
            };

            const render = () => {
                animate();

                requestAnimationFrame(render);
            };

            render();
        
        
            
            const animate = () => {
                // код с анимацией
                ballPosition = ballPosition + 10;
            };

            const render = () => {
                animate();

                requestAnimationFrame(render);
            };

            render();
        
        
            
            const animate = () => {
                // код с анимацией
                ballPosition = ballPosition + 10;
            };

            const render = () => {
                animate();

                requestAnimationFrame(render);
            };

            render();
        
        
Экран #1
Экран #2

Почему так?

Экран #1 (60Hz)
Экран #2 (144Hz)

Разная частота обновления кадров

        
            const animate = () => {
                // код с анимацией
                ballPosition = ballPosition + 10;
            };

            const render = () => {
                animate();

                requestAnimationFrame(render);
            };

            render();
        
        
        
            const animate = () => {
                // код с анимацией
                ballPosition = ballPosition + 10;
            };

            const render = () => {
                animate();

                requestAnimationFrame(render);
            };

            render();
        
        

🤔 Как быть?

💡 Использовать дельту

        
            let lastTime = 0;

            const animate = (timestamp) => {
                if (!lastTime) lastTime = timestamp;
                const delta = (timestamp - lastTime) / 1000;
                lastTime = timestamp;
                // код с анимацией
                ballPosition = ballPosition + 10 * delta;
            };

            const render = (timestamp) => {
                animate(timestamp);

                requestAnimationFrame(render);
            };

            requestAnimationFrame(render);
        
        

            let lastTime = 0;

            const animate = (timestamp) => {
                if (!lastTime) lastTime = timestamp;
                const delta = (timestamp - lastTime) / 1000;
                lastTime = timestamp;
                // код с анимацией
                ballPosition = ballPosition + 10 * delta;
            };

            const render = (timestamp) => {
                animate(timestamp);

                requestAnimationFrame(render);
            };

            requestAnimationFrame(render);
        
        

            let lastTime = 0;

            const animate = (timestamp) => {
                if (!lastTime) lastTime = timestamp;
                const delta = (timestamp - lastTime) / 1000;
                lastTime = timestamp;
                // код с анимацией
                ballPosition = ballPosition + 10 * delta;
            };

            const render = (timestamp) => {
                animate(timestamp);

                requestAnimationFrame(render);
            };

            requestAnimationFrame(render);
        
        

            let lastTime = 0;

            const animate = (timestamp) => {
                if (!lastTime) lastTime = timestamp;
                const delta = (timestamp - lastTime) / 1000;
                lastTime = timestamp;
                // код с анимацией
                ballPosition = ballPosition + 10 * delta;
            };

            const render = (timestamp) => {
                animate(timestamp);

                requestAnimationFrame(render);
            };

            requestAnimationFrame(render);
        
        

            let lastTime = 0;

            const animate = (timestamp) => {
                if (!lastTime) lastTime = timestamp;
                const delta = (timestamp - lastTime) / 1000;
                lastTime = timestamp;
                // код с анимацией
                ballPosition = ballPosition + 10 * delta;
            };

            const render = (timestamp) => {
                animate(timestamp);

                requestAnimationFrame(render);
            };

            requestAnimationFrame(render);
        
        
Экран #1 (60Hz)
Экран #2 (144Hz)

#5 Изображения

Пробуем реализовать

            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.src = imageUrl;
            
        
            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.src = imageUrl;
            
        
            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.src = imageUrl;
            
        
            
                const getColorFromMouseEvent = (event) => {
                    const bounding = canvas.getBoundingClientRect();
                    const x = (event.clientX - bounding.left) * devicePixelRatio;
                    const y = (event.clientY - bounding.top) * devicePixelRatio; 
                    const pixel = ctx.getImageData(x, y, 1, 1);
                    const data = pixel.data;
            
                    return `rgb(${data[0]} ${data[1]} ${data[2]})`;
                };
            
        
            
                const getColorFromMouseEvent = (event) => {
                    const bounding = canvas.getBoundingClientRect();
                    const x = (event.clientX - bounding.left) * devicePixelRatio;
                    const y = (event.clientY - bounding.top) * devicePixelRatio;
                    const pixel = ctx.getImageData(x, y, 1, 1);
                    const data = pixel.data;
            
                    return `rgb(${data[0]} ${data[1]} ${data[2]})`;
                };
            
        
            
                const getColorFromMouseEvent = (event) => {
                    const bounding = canvas.getBoundingClientRect();
                    const x = (event.clientX - bounding.left) * devicePixelRatio;
                    const y = (event.clientY - bounding.top) * devicePixelRatio;
                    const pixel = ctx.getImageData(x, y, 1, 1);
                    const data = pixel.data;
            
                    return `rgb(${data[0]} ${data[1]} ${data[2]})`;
                };
            
        
            
                const getColorFromMouseEvent = (event) => {
                    const bounding = canvas.getBoundingClientRect();
                    const x = (event.clientX - bounding.left) * devicePixelRatio;
                    const y = (event.clientY - bounding.top) * devicePixelRatio;
                    const pixel = ctx.getImageData(x, y, 1, 1);
                    const data = pixel.data;
            
                    return `rgb(${data[0]} ${data[1]} ${data[2]})`;
                };
            
        

Не работает...

Может, консоль что подскажет?

Uncaught SecurityError:
Failed to execute 'getImageData' on 'CanvasRenderingContext2D':
The canvas has been tainted by cross-origin data.
Uncaught SecurityError:
Failed to execute 'getImageData' on 'CanvasRenderingContext2D':
The canvas has been tainted by cross-origin data.
            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.src = imageUrl;
            
        
            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.src = imageUrl;
            
        
            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.src = imageUrl;
            
        
            
                const imageUrl = 'https://pictures.com/1';

                const canvas = document.getElementById('canvas');
                const ctx = canvas.getContext('2d');

                const img = new Image();
                img.addEventListener('load', () => {
                    ctx.drawImage(img, 0, 0);
                });
                img.crossOrigin = 'anonymous';
                img.src = imageUrl;
            
        

В чём суть?

🚔 Security

Нужно явно разрешить

Такие дела

Подытожим

📝 Заметки про канвас

📝 Заметки про канвас

📝 Заметки про канвас

📝 Заметки про канвас

📝 Заметки про канвас

📝 Заметки про канвас

Время шарить ссылочки 🤳🏼

Подписаться

Рома вещает

Дока, с днём рождения!

Рома Ахмадуллин

Дром