PageSourceSearch

https://makeagif.com/js/customiseControls.js?1790085531

js makeagif.com collected 2026-09-24 08:30:19 UTC 22,128 bytes, 681 lines download raw bytes

1/*
2 * fabric.js Controls Extension
3 * for fabric.js current build
4 * Simon Kunz 09.02.2016 for pixolith
5 * Licensed under the MIT license.
6 */
7( function( global ) {
8    'use strict';
9    var fabric = global.fabric || ( global.fabric = {} ),
10        extCompat = '1.6',
11        isVML = function() {
12            return typeof G_vmlCanvasManager !== 'undefined';
13        },
14        degreesToRadians = fabric.util.degreesToRadians,
15        cursorOffset = {
16            mt: 0, // n
17            tr: 1, // ne
18            mr: 2, // e
19            br: 3, // se
20            mb: 4, // s
21            bl: 5, // sw
22            ml: 6, // w
23            tl: 7 // nw
24        };
25
26    if ( global.fabric.version.indexOf( extCompat ) === -1  ) {
27        console.warn( 'this extension might not be fully compatible with your version ' +
28            'of fabric.js (' + global.fabric.version + ').' +
29            'Consider using the latest compatible build of fabric.js' + extCompat
30        );
31    }
32
33    fabric.util.object.extend( fabric.Object.prototype, {
34
35        /**
36         * When true, image icons are loaded via the drawImage method
37         * @type Boolean
38         * @default false
39         */
40
41        useCustomIcons: false,
42
43        /**
44         * Sets a background-color for drawImage operations with transparency
45         * @type string
46         * @default transparent
47         */
48
49        cornerBackgroundColor: 'transparent',
50
51        /**
52         * Sets the shape of the background for drawImage operations with transparency
53         * @type string
54         * @default rect
55         */
56
57        cornerShape: '',
58
59        /**
60         * Inner Padding between Shape Background and drawn Image
61         * @type int
62         * @default rect
63         */
64
65        cornerPadding: 0,
66
67        /**
68         * Set a custom corner icon
69         * @param {Object} obj settings and icon url.
70         * @param callback function
71         */
72
73        customiseCornerIcons: function( obj, callback ) {
74            var setting;
75
76            for ( setting in obj ) {
77                if ( obj.hasOwnProperty( setting ) ) {
78
79                    if ( obj[ setting ].cornerShape !== undefined ) {
80                        this.cornerShape = obj[ setting ].cornerShape;
81                    }
82
83                    if ( obj[ setting ].cornerBackgroundColor !== undefined ) {
84                        this.cornerBackgroundColor = obj[ setting ].cornerBackgroundColor;
85                    }
86
87                    if ( obj[ setting ].borderColor !== undefined ) {
88                        this.borderColor = obj[ setting ].borderColor;
89                    }
90
91                    if ( obj[ setting ].cornerSize !== undefined ) {
92                        this.cornerSize = obj[ setting ].cornerSize;
93                    }
94
95                    if ( obj[ setting ].cornerPadding !== undefined ) {
96                        this.cornerPadding = obj[ setting ].cornerPadding;
97                    }
98
99                    if ( obj[ setting ].icon !== undefined ) {
100                        this.useCustomIcons = true;
101
102                        this.loadIcon( setting, obj[ setting ].icon, function() {
103                            if ( callback && typeof( callback ) === 'function' ) {
104                                callback();
105                            }
106                        } );
107                    }
108                }
109            }
110        },
111
112        /**
113         * loads the icon image as an image src.
114         * @param {Object} corner to load an icon.
115         * @param iconUrl as a string.
116         * @param callback function.
117         */
118
119        loadIcon: function( corner, iconUrl, callback ) {
120            var self = this,
121                icon = new Image();
122
123            icon.onload = function() {
124                self[ corner + 'Icon' ] = this;
125                if ( callback && typeof( callback ) === 'function' ) {
126                    callback();
127                }
128            };
129
130            icon.onerror = function() {
131                fabric.warn( this.src + ' icon is not an image' );
132            };
133
134            if ( iconUrl.indexOf( 'http' ) > -1 ) {
135                icon.crossOrigin = 'Anonymous';
136            }
137
138            icon.src = iconUrl;
139        },
140
141        /**
142         * copy of the setter method for our american friends.
143         * @param {Object} obj containing corner icon urls and settings.
144         */
145
146        customizeCornerIcons: function( obj ) {
147            this.customiseCornerIcons( obj );
148        },
149
150        /**
151         * Draws corners of an object's bounding box.
152         * Requires public properties: width, height
153         * Requires public options: cornerSize, padding
154         * @param {CanvasRenderingContext2D} ctx Context to draw on
155         * @return {fabric.Object} thisArg
156         * @chainable
157         */
158
159        drawControls: function( ctx ) {
160
161            if ( !this.hasControls ) {
162                return this;
163            }
164
165            var wh = this._calculateCurrentDimensions( true ),
166                width = wh.x,
167                height = wh.y,
168                scaleOffset = this.cornerSize,
169                left = -( width + scaleOffset ) / 2,
170                top = -( height + scaleOffset ) / 2,
171                methodName;
172
173            if ( !this.useCustomIcons ) {
174
175                ctx.lineWidth = 1;
176                ctx.globalAlpha = this.isMoving ? this.borderOpacityWhenMoving : 1;
177                ctx.strokeStyle = ctx.fillStyle = this.cornerColor;
178
179                methodName = this.transparentCorners ? 'strokeRect' : 'fillRect';
180            } else {
181                methodName = 'drawImage';
182            }
183
184            ctx.save();
185
186            // top-left
187            this._drawControl( 'tl', ctx, methodName,
188                left,
189                top,
190                this.tlIcon
191            );
192
193            // top-right
194            this._drawControl( 'tr', ctx, methodName,
195                left + width,
196                top,
197                this.trIcon
198            );
199
200            // bottom-left
201            this._drawControl( 'bl', ctx, methodName,
202                left,
203                top + height,
204                this.blIcon
205            );
206
207            // bottom-right
208            this._drawControl( 'br', ctx, methodName,
209                left + width,
210                top + height,
211                this.brIcon
212            );
213
214            if ( !this.get( 'lockUniScaling' ) ) {
215
216                // middle-top
217                this._drawControl( 'mt', ctx, methodName,
218                    left + width / 2,
219                    top,
220                    this.mtIcon
221                );
222
223                // middle-bottom
224                this._drawControl( 'mb', ctx, methodName,
225                    left + width / 2,
226                    top + height,
227                    this.mbIcon
228                );
229
230                // middle-right
231                this._drawControl( 'mr', ctx, methodName,
232                    left + width,
233                    top + height / 2,
234                    this.mrIcon
235                );
236
237                // middle-left
238                this._drawControl( 'ml', ctx, methodName,
239                    left,
240                    top + height / 2,
241                    this.mlIcon
242                );
243            }
244
245            // middle-top-rotate
246            if ( this.hasRotatingPoint ) {
247                this._drawControl( 'mtr', ctx, methodName,
248                    left + width / 2,
249                    top - this.rotatingPointOffset,
250                    this.mtrIcon
251                );
252            }
253
254            ctx.restore();
255
256            return this;
257        },
258
259        /** Draw controls either with background-shape and color (transparency) or plain image (modified core method)
260         * @private
261         * {string} icon url of the control
262         */
263
264        _drawControl: function( control, ctx, methodName, left, top, icon ) {
265            if ( !this.isControlVisible( control ) ) {
266                return;
267            }
268
269            var size = this.cornerSize,
270                cornerBG = this.cornerBackgroundColor || 'black',
271                cornerShape = this.cornerShape || 'rect',
272                cornerPadding = this.cornerPadding || 10 ;
273
274            if ( this.useCustomIcons ) {
275                if ( cornerShape ) {
276                    ctx.globalAlpha = 1;
277                    ctx.fillStyle = cornerBG;
278                    switch ( cornerShape ) {
279                        case 'rect':
280                            ctx.fillRect( left, top, size, size );
281                            if ( icon !== undefined ) {
282                                ctx[ methodName ](
283                                    icon,
284                                    left + cornerPadding / 2,
285                                    top + cornerPadding / 2,
286                                    size - cornerPadding,
287                                    size - cornerPadding
288                                );
289                            }
290                            break;
291                        case 'circle':
292                            ctx.beginPath();
293                            ctx.arc( left + size / 2, top + size / 2, size / 2, 0, 2 * Math.PI );
294                            ctx.fill();
295                            ctx.closePath();
296                            if ( icon !== undefined ) {
297                                ctx[ methodName ](
298                                    icon,
299                                    left + cornerPadding / 2,
300                                    top + cornerPadding / 2,
301                                    size - cornerPadding,
302                                    size - cornerPadding
303                                );
304                            }
305                            break;
306                    }
307                } else {
308                    if ( icon !== undefined ) {
309                        ctx[ methodName ](
310                            icon,
311                            left,
312                            top,
313                            size,
314                            size
315                        );
316                    }
317                }
318            } else {
319                isVML() || this.transparentCorners || ctx.clearRect( left, top, size, size );
320                ctx[ methodName ]( left, top, size, size );
321            }
322
323        }
324    } );
325
326    fabric.util.object.extend( fabric.Canvas.prototype, {
327        /**
328         * When true, actions can be overwritten
329         * @type Boolean
330         * @default false
331         */
332
333        overwriteActions: false,
334
335        /**
336         * When true, cursors are fixed
337         * @type Boolean
338         * @default false
339         */
340
341        fixedCursors: false,
342
343        /**
344         * setter Method for actions and cursors.
345         * @param {Object} obj containing corner action and cursor url/type.
346         */
347
348        customiseControls: function( obj ) {
349            var setting;
350
351            for ( setting in obj ) {
352                if ( obj.hasOwnProperty( setting ) ) {
353                    if ( obj[ setting ].action !== undefined ) {
354                        this.overwriteActions = true;
355                        this.setCustomAction( setting, obj[ setting ].action );
356                    }
357
358                    if ( obj[ setting ].cursor !== undefined ) {
359                        this.fixedCursors = true;
360                        this.setCustomCursor( setting, obj[ setting ].cursor );
361                    }
362                }
363            }
364        },
365
366        /**
367         * loads the icon image as an image src.
368         * @param {Object} corner to load an icon.
369         * @param action as a string.
370         */
371
372        setCustomAction: function( corner, action ) {
373            this[ corner + 'Action' ] = action ;
374        },
375
376        /**
377         * loads the icon image as an image src.
378         * @param {Object} corner to load an icon.
379         * @param cursorUrl as a string.
380         */
381
382        setCustomCursor: function( corner, cursorUrl ) {
383            this[ corner + 'cursorIcon' ] = cursorUrl ;
384        },
385
386        /**
387         * copy of the setter method for our american friends.
388         * @param {Object}
388 obj containing corner action and cursor url/type.
389         */
390
391        customizeControls: function( obj ) {
392            this.customiseControls( obj );
393        },
394
395        /**
396         * @private
397         */
398
399        _getActionFromCorner: function( target, corner, e ) {
400            if ( !corner ) {
401                return 'drag';
402            }
403
404            if ( corner ) {
405                if ( this[ corner + 'Action' ] && this.overwriteActions ) {
406                    switch ( corner ) {
407                        case 'mtr':
408                            return this[ corner + 'Action' ] || 'rotate';
409                        case 'ml':
410                        case 'mr':
411                            if ( e.shiftKey ) {
412                                return e.shiftKey ? 'skewY' : 'scaleX';
413                            }
414                            return this[ corner + 'Action' ];
415                        case 'mt':
416                        case 'mb':
417                            if ( e.shiftKey ) {
418                                return e.shiftKey ? 'skewY' : 'scaleY';
419                            }
420                            return this[ corner + 'Action' ];
421                        default:
422                            return this[ corner + 'Action' ] || 'scale';
423                    }
424                } else {
425                    switch ( corner ) {
426                        case 'mtr':
427                            return 'rotate';
428                        case 'ml':
429                        case 'mr':
430                            return e.shiftKey ? 'skewY' : 'scaleX';
431                        case 'mt':
432                        case 'mb':
433                            return e.shiftKey ? 'skewX' : 'scaleY';
434                        default:
435                            return 'scale';
436                    }
437                }
438            }
439        },
440
441        /**
442         * @private
443         * @param {Event} e Event object
444         * @param {fabric.Object} target
445         */
446        _setupCurrentTransform: function( e, target ) {
447            if ( !target ) {
448                return;
449            }
450
451            var pointer = this.getPointer( e ),
452                corner = target._findTargetCorner( this.getPointer( e, true ) ),
453                action = this._getActionFromCorner( target, corner, e ),
454                origin = this._getOriginFromCorner( target, corner ),
455                _this = this;
456
457            if ( typeof action === 'function' ) {
458                action.call( _this, e, target );
459            }
460
461            this._currentTransform = {
462                target: target,
463                action: action,
464                corner: corner,
465                scaleX: target.scaleX,
466                scaleY: target.scaleY,
467                skewX: target.skewX,
468                skewY: target.skewY,
469                offsetX: pointer.x - target.left,
470                offsetY: pointer.y - target.top,
471                originX: origin.x,
472                originY: origin.y,
473                ex: pointer.x,
474                ey: pointer.y,
475                lastX: pointer.x,
476                lastY: pointer.y,
477                left: target.left,
478                top: target.top,
479                theta: degreesToRadians( target.angle ),
480                width: target.width * target.scaleX,
481                mouseXSign: 1,
482                mouseYSign: 1,
483                shiftKey: e.shiftKey,
484                altKey: e.altKey
485            };
486
487            this._currentTransform.original = {
488                left: target.left,
489                top: target.top,
490                scaleX: target.scaleX,
491                scaleY: target.scaleY,
492                skewX: target.skewX,
493                skewY: target.skewY,
494                originX: origin.x,
495                originY: origin.y
496            };
497
498            if ( action === 'remove' ) {
499                this._removeAction( e, target );
500            }
501
502            if ( action === 'moveUp' ) {
503                this._moveLayerUpAction( e, target );
504            }
505
506            if ( action === 'moveDown' ) {
507                this._moveLayerDownAction( e, target );
508            }
509
510            if ( typeof action === 'object' ) {
511                if ( Object.keys( action )[ 0 ] === 'rotateByDegrees' ) {
512                    this._rotateByDegrees( e, target, action.rotateByDegrees );
513                }
514            }
515
516            this._resetCurrentTransform( e );
517        },
518
519        /**
520         * Custom remove object action
521         * @private
522         * @param {Event} e Event object
523         * @param {fabric.Object} target
524         */
525
526        _removeAction: function( e, target ) {
527            var _this = this;
528            if ( this.getActiveGroup() && this.getActiveGroup() !== 'undefined' ) {
529                this.getActiveGroup().forEachObject( function( o ) {
530                    o.remove();
531                } );
532                this.discardActiveGroup();
533
534                // as of fabric 1.6.3 necessary for reasons..
535                setTimeout( function() {
536                    _this.deactivateAll();
537                }, 0 );
538
539            } else {
540                target.remove();
541
542                setTimeout( function() {
543                    _this.deactivateAll();
544                }, 0 );
545            }
546        },
547
548        /**
549         * Custom move up object action
550         * @private
551         * @param {Event} e Event object
552         * @param {fabric.Object} target
553         */
554
555        _moveLayerUpAction: function( e, target ) {
556            if ( this.getActiveGroup() && this.getActiveGroup() !== 'undefined' ) {
557                this.getActiveGroup().forEachObject( function( o ) {
558                    o.bringForward();
559                } );
560            } else {
561                target.bringForward();
562            }
563        },
564
565        /**
566         * Custom move down object action
567         * @private
568         * @param {Event} e Event object
569         * @param {fabric.Object} target
570         */
571
572        _moveLayerDownAction: function( e, target ) {
573            if ( this.getActiveGroup() && this.getActiveGroup() !== 'undefined' ) {
574                this.getActiveGroup().forEachObject( function( o ) {
575                    o.sendBackwards();
576                } );
577            } else {
578                target.sendBackwards();
579            }
580        },
581
582        /**
583         * Custom move down object action
584         * @private
585         * @param {Event} e Event object
586         * @param {fabric.Object} target
587         * @param {Integer} value of rotation
588         */
589
590        _rotateByDegrees: function( e, target, value ) {
591            var angle = parseInt( target.getAngle() ) + value,
592                needsOriginRestore = false;
593
594            if ( ( target.originX !== 'center' || target.originY !== 'center' ) && target.centeredRotation ) {
595                this._setOriginToCenter( target );
596                needsOriginRestore = true;
597            }
598
599            angle = angle > 360 ? angle - 360 : angle;
600
601            if ( this.getActiveGroup() && this.getActiveGroup() !== 'undefined' ) {
602                this.getActiveGroup().forEachObject( function( obj ) {
603                    obj
604                        .setAngle( angle )
605                        .setCoords();
606                } );
607            } else {
608                target
609                    .setAngle( angle )
610                    .setCoords();
611            }
612
613            if ( needsOriginRestore ) {
614                this._setCenterToOrigin( target );
615            }
616
617            this.renderAll();
618        },
619
620        _setOriginToCenter: function( target ) {
621            target._originalOriginX = target.originX;
622            target._originalOriginY = target.originY;
623
624            var center = target.getCenterPoint();
625
626            target.set( {
627                originX: 'center',
628                originY: 'center',
629                left: center.x,
630                top: center.y
631            } );
632        },
633
634        _setCenterToOrigin: function( target ) {
635            var originPoint = target.translateToOriginPoint(
636            target.getCenterPoint(),
637            target._originalOriginX,
638            target._originalOriginY );
639
640            target.set( {
641              originX: target._originalOriginX,
642              originY: target._originalOriginY,
643              left: originPoint.x,
644              top: originPoint.y
645          } );
646        },
647
648        /**
649         * Sets either the standard behaviour cursors or if fixedCursors is true, tries to set a custom cursor
650         * either by using an icon or a build-in cursor. Cursor icon extensions are matched with a regular expression.
651         * @private
652         * {string} corner name
653         * {target} event handler of the hovered corner
654         */
655        _setCornerCursor: function( corner, target, e ) {
656            var iconUrlPattern = /\.(?:jpe?g|png|gif|jpg|jpeg|svg)$/;
657
658            if ( this.fixedCursors && this[ corner + 'cursorIcon' ] ) {
659                if ( iconUrlPattern.test( this[ corner + 'cursorIcon' ] ) ) {
660                    this.setCursor( 'url(' + this[ corner + 'cursorIcon' ] + '), auto' );
661                } else {
662                    if ( this[ corner + 'cursorIcon' ] === 'resize' ) {
663                        this.setCursor( this._getRotatedCornerCursor( corner, target, e ) );
664                    } else {
665                        this.setCursor( this[ corner + 'cursorIcon' ] );
666                    }
667                }
668            } else {
669                if ( corner in cursorOffset ) {
670                    this.setCursor( this._getRotatedCornerCursor( corner, target, e ) );
671                } else if ( corner === 'mtr' && target.hasRotatingPoint ) {
672                    this.setCursor( this.rotationCursor );
673                } else {
674                    this.setCursor( this.defaultCursor );
675                    return false;
676                }
677            }
678        }
679    } );
680
681} )( typeof exports !== 'undefined' ? exports : this );

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.