Results 1 to 6 of 6

Thread: Custom sprite with image, text and rectangle

  1. #1
    Sencha User Misiu's Avatar
    Join Date
    Jun 2012
    Location
    Poland
    Posts
    260
    Answers
    6

    Default Custom sprite with image, text and rectangle

    I'm trying to build graph and add users information to it.
    Right now I must create 3 sprites for every user and position them one by one:

    PHP Code:
            Ext.create('Ext.Container', {
                
    renderToExt.getBody(),
                
    width600,
                
    height400,
                
    layout'fit',
                
    items: {
                    
    xtype'draw',
                    
    sprites: [{
                        
    type'rect',
                        
    x50,
                        
    y50,
                        
    width150,
                        
    height50,
                        
    radius25,
                        
    fillStyle'blue'
                    
    }, {
                        
    type'text',
                        
    x100,
                        
    y80,
                        
    text'Username',
                        
    fontSize18,
                        
    fillStyle'white'
                    
    }, {
                        
    type'image',
                        
    x60,
                        
    y55,
                        
    width40,
                        
    height40,
                        
    src'https://cdn4.iconfinder.com/data/icons/meBaze-Freebies/512/user.png'
                    
    }]
                }
            }); 
    How can I create custom sprite that will render those elements using only one item. Ideally I would like to use it like so:

    PHP Code:
            Ext.create('Ext.Container', {
                
    renderToExt.getBody(),
                
    width600,
                
    height400,
                
    layout'fit',
                
    items: {
                    
    xtype'draw',
                    
    sprites: [{
                        
    type'user',
                        
    x50,
                        
    y50,
                        
    width150,
                        
    height50,
                        
    name'username',
                        
    icon'https://cdn4.iconfinder.com/data/icons/meBaze-Freebies/512/user.png'
                    
    }]
                }
            }); 
    Can someone show how to define such complex sprite? Should I extend Ext.draw.sprite.Sprite? Or maybe I should use Ext.draw.sprite.Composite?

    Here is my fiddle:

  2. #2
    Sencha User Misiu's Avatar
    Join Date
    Jun 2012
    Location
    Poland
    Posts
    260
    Answers
    6

    Default

    Anyone?

  3. #3
    Sencha User Misiu's Avatar
    Join Date
    Jun 2012
    Location
    Poland
    Posts
    260
    Answers
    6

    Default

    I must extend Sprite?
    Even a tiny example is welcome.

  4. #4
    Ext JS Premium Member
    Join Date
    Oct 2009
    Posts
    95
    Answers
    8

    Default

    I've never used the Draw package before, though I've been meaning to. In any case, the following worked for me in fiddle but I'm not sure it's what you're looking for.

    Code:
    Ext.define('Fiddle.sprites.User',{
        extend:'Ext.draw.sprite.Composite',
        alias:'sprite.usersprite',
        
        constructor:function(){
            this.callParent(arguments);
            
            this.add({
                type: 'rect',
                x: 50,
                y: 50,
                width: 150,
                height: 50,
                radius: 25,
                fillStyle: 'blue'
             });
            this.add({
                type: 'text',
                x: 100,
                y: 80,
                text: 'Username',
                fontSize: 18,
                fillStyle: 'white'
             });
            this.add({
                type: 'image',
                x: 60,
                y: 55,
                width: 40,
                height: 40,
                src: 'https://cdn4.iconfinder.com/data/icons/meBaze-Freebies/512/user.png'
            });
            
        }
    })
    
    
    Ext.application({
        name: 'Fiddle',
    
        launch: function() {
            Ext.create('Ext.Container', {
                renderTo: Ext.getBody(),
                width: 600,
                height: 400,
                layout: 'fit',
                items: {
                    xtype: 'draw',
                    sprites:[{
                        type:'usersprite',
                        translateX:150,//use translateX/Y to move the composite image around
                        translateY:150
                    }]
                }
            });
        }
    });

  5. #5
    Sencha - Ext JS Dev Team vitalyx's Avatar
    Join Date
    Jul 2012
    Posts
    40
    Answers
    2

    Default

    There you go. This will be a Charts KS example when Ext 5.0.2 is released.
    This will render the following sprite given just two points:
    Screen Shot 2014-10-21 at 12.00.30 PM.png

    Code:
    Ext.define('KitchenSink.view.draw.Protractor', {
        // Typically, you'd want to extend the Composite sprite instead of using it directly.
        extend: 'Ext.draw.sprite.Composite',
        alias: 'sprite.protractor',
    
    
        inheritableStatics: {
            def: {
                // And define your own attributes on the composite that abstract away
                // the actual implementation.
                processors: {
                    // The first four attributes (start and end point coordinates)
                    // is all we really need for this sprite to work.
                    fromX: 'number',
                    fromY: 'number',
                    toX: 'number',
                    toY: 'number',
                    // The rest of the attributes is just to allow customization.
                    baseLineLength: 'number',
                    arcRadius: 'number',
                    arrowLength: 'number',
                    arrowAngle: 'number'
                },
                // Changes to composite attributes will then trigger the recalculation of
                // attributes of composite's children sprites.
                // Here we define which composite's attributes should trigger such recalculation.
                // In this case we use a single updater function called 'recalculate', but it's
                // possible to specify and use different updaters for different attributes.
                dirtyTriggers: {
                    fromX: 'recalculate',
                    fromY: 'recalculate',
                    toX: 'recalculate',
                    toY: 'recalculate',
                    baseLineLength: 'recalculate',
                    arcRadius: 'recalculate',
                    arrowLength: 'recalculate',
                    arrowAngle: 'recalculate'
                },
                // Default values of composite's attributes.
                defaults: {
                    fromX: 0,
                    fromY: 0,
                    toX: 100,
                    toY: 100,
                    baseLineLength: 50,
                    arcRadius: 40,
                    arrowLength: 10,
                    arrowAngle: Math.PI / 8
                },
                updaters: {
                    // This updater function is called every time the attributes
                    // of the composite change, including animations.
                    // Inside this updater we calculate and set the values of the attributes
                    // of the children of the composite based on the values of the composite's
                    // attributes.
                    recalculate: function (attr) {
                        // Please see this ticket https://sencha.jira.com/browse/EXTJS-15521
                        // for a graphical representation of what's going on in this function.
                        var me = this,
                            fromX = attr.fromX,
                            fromY = attr.fromY,
                            toX = attr.toX,
                            toY = attr.toY,
                            dx = toX - fromX,
                            dy = toY - fromY,
                            PI = Math.PI,
                            radius = Math.sqrt(dx*dx + dy*dy);
    
    
                        if (dx === 0 || dy === 0) {
                            return;
                        }
    
    
                        var alpha = Math.atan2(dy, dx),
                            sin = Math.sin,
                            cos = Math.cos,
                            arcRadius = attr.arcRadius,
                            beta = PI - attr.arrowAngle,
                            x = attr.arrowLength * cos(beta),
                            y = attr.arrowLength * sin(beta),
                            // Coordinates of the arc arrow tip.
                            ax = arcRadius * cos(alpha) + fromX,
                            ay = arcRadius * sin(alpha) + fromY,
                            mat = Ext.draw.Matrix.fly([cos(alpha), sin(alpha), -sin(alpha), cos(alpha), toX, toY]),
                            angleArrowThreshold = Ext.draw.Draw.radian * me.getAngleArrowThreshold(),
                            isSmallAngle = alpha < angleArrowThreshold && alpha > -angleArrowThreshold,
                            angleTextRadius = arcRadius * 1.2,
                            isSmallRadius = radius < angleTextRadius,
                            radiusTextFlip, fontSize,
                            theta = 0;
    
    
                        if (alpha > 0) {
                            theta = alpha + PI / 2 - attr.arrowAngle / (arcRadius * 0.1);
                        } else if (alpha < 0) {
                            theta = alpha - PI / 2 + attr.arrowAngle / (arcRadius * 0.1);
                        }
    
    
                        me.createSprites();
    
    
                        me.baseLine.setAttributes({
                            fromX: fromX,
                            fromY: fromY,
                            toX: fromX + attr.baseLineLength,
                            toY: fromY,
                            hidden: isSmallRadius
                        });
                        me.radiusLine.setAttributes({
                            fromX: fromX,
                            fromY: fromY,
                            toX: toX,
                            toY: toY,
                            strokeStyle: attr.strokeStyle
                        });
                        me.radiusArrowLeft.setAttributes({
                            fromX: toX,
                            fromY: toY,
                            toX: mat.x(x, y),
                            toY: mat.y(x, y),
                            strokeStyle: attr.strokeStyle
                        });
                        me.radiusArrowRight.setAttributes({
                            fromX: toX,
                            fromY: toY,
                            toX: mat.x(x, -y),
                            toY: mat.y(x, -y),
                            strokeStyle: attr.strokeStyle
                        });
    
    
                        mat = Ext.draw.Matrix.fly([cos(theta), sin(theta), -sin(theta), cos(theta), ax, ay]);
    
    
                        me.angleLine.setAttributes({
                            startAngle: 0,
                            endAngle: alpha,
                            cx: fromX,
                            cy: fromY,
                            r: arcRadius,
                            anticlockwise: alpha < 0,
                            hidden: isSmallRadius
                        });
                        me.angleArrowLeft.setAttributes({
                            fromX: ax,
                            fromY: ay,
                            toX: mat.x(x, y),
                            toY: mat.y(x, y),
                            hidden: isSmallAngle || isSmallRadius
                        });
                        me.angleArrowRight.setAttributes({
                            fromX: ax,
                            fromY: ay,
                            toX: mat.x(x, -y),
                            toY: mat.y(x, -y),
                            hidden: isSmallAngle || isSmallRadius
                        });
                        me.angleText.setAttributes({
                            x: angleTextRadius * cos(alpha / 2) + fromX,
                            y: angleTextRadius * sin(alpha / 2) + fromY,
                            text: me.getAngleText() + ': ' + (alpha * 180 / PI).toFixed(me.getPrecision()) + '',
                            hidden: isSmallRadius
                        });
                        radiusTextFlip = ((alpha > -0.5 * PI && alpha < 0.5 * PI) || (alpha > 1.5 * PI && alpha < 2 * PI)) ? 1 : -1;
                        fontSize = parseInt(me.radiusText.attr.fontSize, 10);
                        x = 0.5 * radius * cos(alpha) + fromX + radiusTextFlip * fontSize * sin(alpha);
                        y = 0.5 * radius * sin(alpha) + fromY - radiusTextFlip * fontSize * cos(alpha);
                        me.radiusText.setAttributes({
                            x: x,
                            y: y,
                            rotationRads: alpha,
                            rotationRads: radiusTextFlip === 1 ? alpha : alpha - PI,
                            rotationCenterX: x,
                            rotationCenterY: y,
                            text: me.getRadiusText() + ': ' + radius.toFixed(me.getPrecision()),
                            hidden: isSmallRadius
                        });
                    }
                }
            }
        },
    
    
        // Additional configuration options that are meant to be used once during setup time.
        // These need not be attributes, because we don't need them to animate
        // or trigger changes in other attributes.
        config: {
            radiusText: 'length',
            angleText: 'angle',
            precision: 1,
            angleArrowThreshold: 15
        },
    
    
        createSprites: function () {
            var me = this;
            // Only create sprites if they haven't been created yet.
            if (!me.baseLine) {
                me.baseLine = me.add({
                    type: 'line',
                    lineDash: [2, 2]
                });
                me.radiusLine = me.add({
                    type: 'line'
                });
                // Left line of the radius arrow.
                me.radiusArrowLeft = me.add({
                    type: 'line'
                });
                // Right line of the radius arrow.
                me.radiusArrowRight = me.add({
                    type: 'line'
                });
                me.angleLine = me.add({
                    type: 'arc',
                    strokeStyle: 'black',
                    lineDash: [2, 2]
                });
                // Left line of the angle arrow.
                me.angleArrowLeft = me.add({
                    type: 'line',
                    lineDash: [2, 2]
                });
                // Right line of the angle arrow.
                me.angleArrowRight = me.add({
                    type: 'line',
                    lineDash: [2, 2]
                });
                me.radiusText = me.add({
                    type: 'text',
                    textAlign: 'center',
                    textBaseline: 'middle',
                    font: '12px'
                });
                me.angleText = me.add({
                    type: 'text',
                    textBaseline: 'middle',
                    font: '12px'
                });
            }
        },
    
    
        constructor: function () {
            var me = this;
            // The 'recalculate' updater will be called at the time of the 'callParent' call.
            // But the children sprites have not been created and added to the composite yet.
            // We can't add children to the composite before the 'callParent' call
            // because the composite hasn't been initialized yet.
            // And adding them after the 'callParent' call is too late, because the
            // 'recalculate' updater needs them.
            // So we define the 'createSprites' function that is called inside the 'recalculate'
            // updater before the sprites are used.
            me.callParent(arguments);
        }
    });

  6. #6
    Sencha - Ext JS Dev Team vitalyx's Avatar
    Join Date
    Jul 2012
    Posts
    40
    Answers
    2

    Default

    You may also find this drawing useful to follow the reasoning in the 'recalculate' updater. Although this is not required if you just want to know how to use composite sprites.

    Screen Shot 2014-10-21 at 12.05.09 PM.jpg

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •