Rysowanie po Canvas

0

Witam
Na początek kod programu dość krótki i nieskomplikowanty:

<html>
<head>
<script type="text/javascript" src="jquery-1.7.1.min.js"></script>

<script type = "text/javascript">

    function Star(context, x, y, imageSrc, angle, angleStep) {
        this.context = context;
        this.x = x;
        this.y = y;
        this.image = new Image();
        this.image.src = imageSrc;
        this.angle = angle;
        this.angleStep = angleStep;

        this.nextFrame = function () { this.angle += this.angleStep; }

        this.draw = function () {

            this.context.save();

            this.context.translate(this.image.width / 2, this.image.height / 2);

            this.context.translate(this.x, this.y);

            this.context.rotate(this.angle);
            this.context.scale(0.3, 0.3);

            this.context.drawImage(this.image, -this.image.width / 2, -this.image.height / 2);

            this.context.restore();

        }
    }

    $(document).ready(function () {
        var clicked = false;

        var canvas = document.getElementById("gameCanvas");
        var context = canvas.getContext("2d");

        var stars = new Array();

        display(context, stars);
        logic(stars);

        $("#gameCanvas").mousedown(function (e) {

            var m = getMousePos(canvas, e);
            stars[stars.length] = new Star(context, m.x - 144, m.y - 144, "star.png", 0, Math.random() / 40);
            $("#starsCount").html(stars.length);
            clicked = true;
        });
        $("#gameCanvas").mouseup(function (e) {
            clicked = false;
        });

        $("#gameCanvas").mousemove(function (e) {

            if (clicked) {
                var m = getMousePos(canvas, e);
                stars[stars.length] = new Star(context, m.x - 144, m.y - 144, "star.png", 0, Math.random() / 40);
                $("#starsCount").html(stars.length);
            }

        });

    });

    function logic(stars) {
        setInterval(function () {
            for (var x = 0; x < stars.length; x++) {
                stars[x].nextFrame();

            }

        }, 20);
    }

    function display(context, stars) {
        setInterval(function () {
            var img = new Image();
            img.src = "bg.jpg";
            context.drawImage(img, 0, 0, 800, 600);
            for (var x = 0; x < stars.length; x++) {
                stars[x].draw();

            }

        }, 1000/30);
    }

    function getMousePos(canvas, evt) {
        // get canvas position
        var obj = canvas;
        var top = 0;
        var left = 0;
        while (obj && obj.tagName != 'BODY') {
            top += obj.offsetTop;
            left += obj.offsetLeft;
            obj = obj.offsetParent;
        }

        // return relative mouse position
        var mouseX = evt.clientX - left + window.pageXOffset;
        var mouseY = evt.clientY - top + window.pageYOffset;
        return {
            x: mouseX,
            y: mouseY
        };
    }
   
</script>

</head>

<body>
    <div id="container" style="margin: 0 auto;  text-align:center">
        <canvas id="gameCanvas" width="800" height="600" style="background-color: Red">
    	    Twoja przeglądarka nie obsługuje canvas.
        </canvas>
    </div>
    <div id="starsCount"></div>
</body>

</html>

Problem polega na tym, że przy około 100 narysowanych gwiazdkach (rozmiar obrazka to 288x288, PNG) zaczyna strasznie mulić canvasa - spada drastycznie ilość rysowanych klatek na sekundę.
Czy ktoś wie jak zoptymalizować takie rysowanie po canvasie w Html5?

0

powinieneś cache'ować przetransponowane obrazki w canvasach
nie rysuj za każdym razem obrazka, narysuj go raz i zapamiętaj - te obiekty Star powinny zawierać własny canvas, nie Image
jeżeli obrazki mają się obracać to zapamiętaj wszystkie obroty jako animację - w jednym canvasie koło siebie w fazie ładowania narysuj wszystkie możliwe obroty a potem rysuj tylko odpowiednie fragmenty tego canvasa - możesz oszczędzić jednocześnie na pamięci i obliczeniach

jeżeli obrazki są statyczne to też możesz je przerenderować raz i zapamiętać w canvasie - możesz je wrenderować w tło
tło powinno być w drugim fizycznym canvasie pod spodem - nie musisz go wtedy odrysowywać w każdej klatce

nie musisz też odrysowywać wszystkich gwiazdek w każdej klatce - jeżeli coś ma się obracać cztery razy wolniej to może niech po prostu się obraca co czwartą klatkę...

przede wszystkim - nie bój się używać wirtualnych canvasów, funkcje translate, rotate, scale a przede wszystkim draw to Twoi wrogowie - używaj jak najmniej i jak najrzadziej
grupuj obrazki, wykorzystuj sztuczki - jeżeli gwiazdek ma być kilka tysięcy to tak naprawdę potrzebne Ci góra parę animowanych powtarzających się canvasów a nikt nie dostrzeże schematu

nie używaj też setInterval - użyj requestAnimationFrame - wtedy silnik przeglądarki jest Ci w stanie zoptymalizować działanie pod animacje, zsynchronizować wyświetlanie z odświeżaniem obrazu i zapobiec zbędnym obliczeniom kiedy canvas nie jest widoczny - sama ta zmiana powoduje u mnie dwukrotne zwiększenie płynności (mniejszy interwał w setInterval niczego nie zmienia)

ogólnie masz dwie pętle - logiki i renderowania które ze sobą nie współgrają
powinieneś mieć jedną funkcję, która będzie wywoływała update() a zaraz po tym draw() - po co funkcji "logic" mniejszy interwał skoro zmian i tak nie będzie widać? wywołując dwa razy setInterval wcale nie tworzysz dwóch wątków - javascript działa jednowątkowo i tylko jeden fragment kodu wywołuje się na raz - tak naprawdę twoje funkcje i tak się wywołują na zmianę

obsługi wejścia też nie rób w ten sposób - zapamiętuj kliknięcia myszką i wejście klawiatury a obsługę tych zdarzeń rób synchronicznie w funkcji update()

1 użytkowników online, w tym zalogowanych: 0, gości: 1