kittencup / angular2-ama-cn

angular2 随便问
691 stars 101 forks source link

想请教一下ng2关于每个component的调用执行过程~ #123

Open Naixor opened 8 years ago

Naixor commented 8 years ago

遇到一个奇怪的问题:component内的canvas元素只能通过setTimeout这种跳出当前执行循环的方法才能drawImage成功。

@Component({
    selector: 'magic-image',
    template : '<canvas #canvas [width]="width" [height]="height"></canvas>', 
    providers: [
                    HTTP_PROVIDERS,
                    provide(BrowserXhr, {
                        useClass: ExtenBrowserXhr
                    }),
                    Base64Coder
                ]
})

export class MagicImage {
    src: string = '';
    width: number = 0;
    height: number = 0;
    lockRatio: string = 'min';
    context: CanvasRenderingContext2D = null;
    @ViewChild('canvas') canvas: ElementRef;
    constructor(
        @Inject(Http) http,
        @Inject(Base64Coder) base64,
        @Attribute('src') src: string,
        @Attribute('width') width: number,
        @Attribute('height') height: number,
        @Attribute('lockRatio') lockRatio: string
    ) {
        this.src = src;
        this.width = width || 0;
        this.height = height || 0;
        // http.get(this.src).subscribe(response => {
        //     console.log(window.btoa(response.text()).length);
        // });
    }
    drawImage(image) {
        if (!this.context) {
            this.context = this.canvas.nativeElement.getContext('2d');
        }
        this.context.drawImage(image, 0, 0);
    }
    ngAfterViewInit(){
        this.context = this.canvas.nativeElement.getContext('2d');
        let self = this;
        let img = new Image();
        img.onload = function () {
            self.width = img.width;
            self.height = img.height;
            // self.drawImage(img);
            setTimeout(function () {
                this.drawImage(img);
            }.bind(self), 0);
        }
        img.src = this.src;
        // console.log(this.canvas.nativeElement);
    }
}

很奇怪ng2内部的加载运行机制,正在看然而并没有找到其中的端倪

kittencup commented 8 years ago
function () {
            self.width = img.width;
            self.height = img.height;
            self.drawImage(img);
 }

要理解。。 当设置了self.width 只是设置了当前对象的width属性,而不是canvas对象的width属性。

当此函数结束后(drawImage也执行完了),ng2的zone会触发change_detection,然后检测到width和height属性有变化,然后去渲染view上的canvas,这时候canvas才会有width和height ,这导致drawImage在canvas有width和height之前

function () {
            self.width = img.width;
            self.height = img.height;
            setTimeout(function () {
                this.drawImage(img);
            }.bind(self), 0);
 }

而这样则不同,function会触发一次change detection ,setTimeout内的会在下一次触发change detection

所以drawImage会在canvas实际有width和height之后执行

使用下面方法可以解决该问题

import {Component,ViewChild,ElementRef} from 'angular2/core';

@Component({
    selector: 'app',
    template: `
        <h1>app</h1>
        <canvas #canvas></canvas>
  `,
})
export class App {

    @ViewChild('canvas') canvas: ElementRef;
    context: CanvasRenderingContext2D = null;

    ngAfterViewInit(){
        this.context = this.canvas.nativeElement.getContext('2d');

        let img = new Image();

        img.onload =  () => {
            this.canvas.nativeElement.width = img.width;
            this.canvas.nativeElement.height = img.height;

            this.context.drawImage(img, 0, 0);
        };

        img.src = 'https://www.google.co.jp/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png';
    }
}
Naixor commented 8 years ago

感谢,我去看下zone,那没有ng1中类似scope.$apply()这种的强制触发一次change的嘛?

kittencup commented 8 years ago

@Naixor 请不要在1个issue里问2个问题,已帮你移到 https://github.com/kittencup/angular2-ama-cn/issues/126