Class sample - garevna/js-course GitHub Wiki

☕ Пример

В этом примере мы будем работать с svg-графикой

SVG Tutorial:
🔗 w3schools 🔗 htmlacademy

createElementNS()

☝ Для динамического создания элементов SVG нужно использовать метод createElementNS()
с указанием ссылки на пространство имен ( NS )

Это необходимо для того, чтобы браузер правильно понимал и отображал svg-элементы

SVG - это тип XML-разметки, который имеет собственное пространство имен, которое может быть встроено в HTML5

✅ Первым аргументом метода createElementNS() идет ссылка на пространство имен
( http://www.w3.org/2000/svg )
✅ Второй аргумент - имя тега элемента в этом пространстве имен

✋ Если использовать обычный метод createElement(), то браузер будет интерпретировать его в пространстве имен HTML
( по умолчанию )

✋ Для корректной работы svg-элементов нужно, чтобы браузер интерпретировал их в пространстве имен SVG

Например, чтобы корректно создать контейнер для svg-графики:

document.createElementNS ( "http://www.w3.org/2000/svg", "svg" )
Проверим в консоли:
let x = document.createElement ( "svg" )
x.namespaceURI  // "http://www.w3.org/1999/xhtml"

let x = document.createElementNS ( "http://www.w3.org/2000/svg", "svg" )
x.namespaceURI  // "http://www.w3.org/2000/svg"
Valid Namespace URIs:
 ✅ HTML - http://www.w3.org/1999/xhtml
 ✅ SVG - http://www.w3.org/2000/svg

Базовый класс

Создадим класс DrawFigures, который будет создавать элемент svg
с двумя методами: setSize() и drawFigure()

✍ Метод setSize() будет изменять размеры элемента svg
✍ Метод drawFigure() будет добавлять элементы в контейнер svg

Имя элемента будет передано первым аргументом метода  ( figure )
возможные значения  "line", "circle", "path", "rect" и т.д.
Параметры фигуры будут переданы вторым аргументом метода 
( params )

✍ Поскольку у каждого элемента svg свой набор атрибутов, создаем свойство attrs ( объект ), свойства которого будут именами svg-элементов, а значения - массивом атрибутов каждого svg-элемента

При создании svg-элемента его атрибуты будут установлены с помощью метода setAttribute()

const DrawFigures = class SVG {
    constructor ( w, h ) {
        this.canvas = document.createElementNS ( "http://www.w3.org/2000/svg", "svg" )
        document.body.appendChild ( this.canvas )
        this.setSize ( w, h )
        this.attrs = {
            line: [ "x1", "y1", "x2", "y2" ],
            circle: [ "cx", "cy", "r" ]
        }
    }
    setSize ( w, h ) {
        this.canvas.setAttribute ( "width", w )
        this.canvas.setAttribute ( "height", h )
    }
    drawFigure ( figure, params ) {
        var elem = document.createElementNS ( "http://www.w3.org/2000/svg", figure )
        this.canvas.appendChild ( elem )
        for ( var x of this.attrs [ figure ] ) {
            elem.setAttribute ( x, params [ x ] )
        }
        return elem
    }
}
Протестировать работу класса можно так:
let sample = new DrawFigures( 300, 300 )
sample.drawFigure ( "line", { x1: 10, y1: 20, x2: 250, y2: 250 } )
                        .setAttribute ( "stroke", "red" )

Вызов метода drawFigure() создаст элемент <line> и вернет ссылку на него,
но этот элемент не отобразится на странице, поскольку в массиве attrs.line
нет атрибута "stroke", задающего цвет линии

✏️ Чтобы увидеть этот элемент на странице, нам приходится устанавливать значение атрибута stroke
после вызова метода drawFigure():

setAttribute ( "stroke", "red" )

✏️ Теперь можно рисовать и другие фигуры, и настраивать их атрибуты:

var circle = sample.drawFigure ( "circle", { cx: 180, cy: 180, r: 150 } )
circle.setAttribute ( "stroke", "blue" )
circle.setAttribute ( "fill", "transparent" )
sample.setSize ( 400, 400 )
circle.setAttribute ( "stroke-width", 8 )

☕ Пример в песочнице

Дочерний класс

Теперь создадим дочерний класс ColoredFigures,
расширяющий функционал родительского класса DrawFigures
путем добавления атрибутов линий и заливки фигур
( "stroke", "style", "fill" )
и метода удаления элемента erase

В конструкторе дочернего класса вызовем метод super(),
чтобы был создан контейнер с нужными размерами,

Метод super() должен быть вызван первым в конструкторе,
поскольку до его вызова значение this не будет определено
внутри конструктора

Кроме того, расширим функционал базового класса DrawFigures,
добавив атрибуты "stroke", "style" и "fill" это мы тоже сделаем в конструкторе класса ColoredFigures

class ColoredFigures extends DrawFigures {
    constructor () {
        super ( window.innerWidth - 20, window.innerHeight - 20 )
        this.figures = []
        for ( let x in this.attrs ) {
            this.attrs [x].push ( "stroke", "style", "fill" )
        }
    }
    line ( params, line ) {
        this.draw ( "line", params )
    }
    circle ( params, line, fill ) {
        this.draw ( "circle", params )
    }
    draw ( figure, params ) {
        if ( params.strokeWidth ) {
            Object.assign ( params, { 
                style: `stroke-width: ${ params.strokeWidth }` 
            } )
            delete params.strokeWidth
        }
        var fig = this.drawFigure ( figure, params )
        this.figures.push ( fig )
    }
    erase ( figure ) {
        if ( figure > this.figures.length - 1 || figure < 0 ) return
        var deleted = this.figures [ figure ]
        deleted.parentNode.removeChild ( deleted )
        this.figures.splice ( figure, 1 )
    }
}
Проверим, как работает расширенный класс ColoredFigures
let canvas = new ColoredFigures ( 400, 500 )

canvas.line ( { 
        x1: 10, 
        y1: 250, 
        x2: 250, 
        y2: 50, 
        stroke: "green", 
        strokeWidth: 5 
} )
canvas.circle ( { 
        cx: 150, 
        cy: 150, 
        r: 100, 
        fill: "#ff00ff90", 
        stroke: "#909", strokeWidth: 10 
} )

Конечно, мы можем создавать элементы, обращаясь к методу базового класса drawFigure():

canvas.drawFigure ( "line", { 
        x1: 200, 
        y1: 150, 
        x2: 50, 
        y2: 100, 
        stroke: "blue", 
        style: "stroke-width: 10" 
} )

но тогда созданные svg-элементы не попадут в массив figures
и их нельзя будет удалить с помощью метода erase()

Кроме того, вызов метода line() или circle() лаконичнее

☝ Обратите внимание, 
что у класса  ColoredFigures  
есть свойство  prototype,
которого нет ( и не может быть ) у экземпляра

В свойстве  prototype  
класса  ColoredFigures  
находятся методы  circle(),
line(),  draw()  и   erase() 

☝ У класса  ColoredFigures  
есть также свойство  __proto__ 
это ссылка на родительский класс SVG

☝ У родительского класса, 
как и следовало ожидать, 
тоже есть свойство prototype,
и в этом свойстве находятся 
методы drawFigure() и setSize()

☝ Ни у класса ColoredFigures,  
ни у класса SVG  
нет свойств attrs, canvas и figures
Они есть только 
у экземпляра этого класса

☕ 1 ☕ 2 (с наворотами)

⚠️ **GitHub.com Fallback** ⚠️