This library was generated with Angular CLI version 10.0.14.
Check out an example here
The masonry grid is hastily calculated using sdMasonryWidth
and sdMasonryHeight
on each item on an array that you provide. Much faster than working this out on the fly after render that you see on other masonry layout packages. From there, you may add as much or as little data you want to each item which will be passed to one of your own component to do with what you will.
Did I mention sd-masonry also flawlessly handles pagination. Simply add more items to your data
object
npm i sd-masonry
Import SdMasonryModule
into your app's modules:
import { SdMasonryModule } from 'sd-masonry';
@NgModule({
imports: [SdMasonryModule],
...
})
@Params
component
: Component Class (required)data
: array (required)maxGridGap
: number (optional - default 10px)maxTotalColCount
: number (optional - default 10px)
<sd-masonry [component]="component" [data]="data" maxGridGap="0" maxTotalColCount="15"></sd-masonry>
maxGrigGap
and maxTotalColCount
will automatically reduce in size depending on the container size
Your component will be passed each item in your data
array above in the form of an item
object. Your component will need to be ready to grab it - @Input() item
@Input() item: {sdMasonryWidth: number, sdMasonryHeight: number, ... };
Start a new project ng new my-sd-masonry
Install sd-masonry
cd my-sd-masonry
npm i sd-masonry
replace app.component.ts
with the following
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-root',
template: `<div style="margin: 40px 100px;"><sd-masonry [component]="component" [data]="data"></sd-masonry></div>`,
})
export class AppComponent {
/** Required by sd-masonry */
public component = ItemComponent; /** ItemComponent declared below */
/** Required by sd-masonry (id and url properties are only required by ItemComponent in this example) */
public data: {sdMasonryWidth: number, sdMasonryHeight: number, id?: number, url?: string}[];
private page1 = [
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 1, url: 'https://via.placeholder.com/1000x1500/947/000/?text=1+-+1000x1500' },
{ sdMasonryWidth: 1512, sdMasonryHeight: 1006, id: 2, url: 'https://via.placeholder.com/1512x1006/69c/000/?text=2+-+1512x1006' },
{ sdMasonryWidth: 1507, sdMasonryHeight: 1004, id: 3, url: 'https://via.placeholder.com/1507x1004/a5b/000/?text=3+-+1507x1004' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 4, url: 'https://via.placeholder.com/1000x1500/3c2/000/?text=4+-+1000x1500' },
{ sdMasonryWidth: 1086, sdMasonryHeight: 1629, id: 5, url: 'https://via.placeholder.com/1086x1629/ae4/000/?text=5+-+1086x1629' },
{ sdMasonryWidth: 1376, sdMasonryHeight: 1720, id: 6, url: 'https://via.placeholder.com/1376x1720/5c7/000/?text=6+-+1376x1720' },
{ sdMasonryWidth: 1488, sdMasonryHeight: 992, id: 7, url: 'https://via.placeholder.com/1488x992/dbd/000/?text=7+-+1488x992' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1351, id: 8, url: 'https://via.placeholder.com/1000x1351/c8b/000/?text=8+-+1000x1351' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 9, url: 'https://via.placeholder.com/1000x1500/2b3/000/?text=9+-+1000x1500' },
{ sdMasonryWidth: 1374, sdMasonryHeight: 917, id: 10, url: 'https://via.placeholder.com/1374x917/468/000/?text=10+-+1374x917' },
{ sdMasonryWidth: 708, sdMasonryHeight: 1060, id: 11, url: 'https://via.placeholder.com/708x1060/2c7/000/?text=11+-+708x1060' },
{ sdMasonryWidth: 1368, sdMasonryHeight: 912, id: 12, url: 'https://via.placeholder.com/1368x912/a3a/000/?text=12+-+1368x912' },
{ sdMasonryWidth: 888, sdMasonryHeight: 1291, id: 13, url: 'https://via.placeholder.com/888x1291/7ec/000/?text=13+-+888x1291' },
{ sdMasonryWidth: 912, sdMasonryHeight: 1368, id: 14, url: 'https://via.placeholder.com/912x1368/1e3/000/?text=14+-+912x1368' },
{ sdMasonryWidth: 1693, sdMasonryHeight: 1123, id: 15, url: 'https://via.placeholder.com/1693x1123/989/000/?text=15+-+1693x1123' },
{ sdMasonryWidth: 1326, sdMasonryHeight: 1988, id: 16, url: 'https://via.placeholder.com/1326x1988/583/000/?text=16+-+1326x1988' },
{ sdMasonryWidth: 1200, sdMasonryHeight: 1500, id: 17, url: 'https://via.placeholder.com/1200x1500/b9e/000/?text=17+-+1200x1500' },
{ sdMasonryWidth: 1734, sdMasonryHeight: 1771, id: 18, url: 'https://via.placeholder.com/1734x1771/889/000/?text=18+-+1734x1771' },
{ sdMasonryWidth: 817, sdMasonryHeight: 561, id: 19, url: 'https://via.placeholder.com/817x561/a5a/000/?text=19+-+817x561' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 20, url: 'https://via.placeholder.com/1000x1500/5bd/000/?text=20+-+1000x1500' },
{ sdMasonryWidth: 931, sdMasonryHeight: 1396, id: 21, url: 'https://via.placeholder.com/931x1396/d69/000/?text=21+-+931x1396' },
{ sdMasonryWidth: 1006, sdMasonryHeight: 1512, id: 22, url: 'https://via.placeholder.com/1006x1512/2bb/000/?text=22+-+1006x1512' },
{ sdMasonryWidth: 1146, sdMasonryHeight: 764, id: 23, url: 'https://via.placeholder.com/1146x764/68e/000/?text=23+-+1146x764' },
{ sdMasonryWidth: 1840, sdMasonryHeight: 1228, id: 24, url: 'https://via.placeholder.com/1840x1228/8bd/000/?text=24+-+1840x1228' },
{ sdMasonryWidth: 750, sdMasonryHeight: 500, id: 25, url: 'https://via.placeholder.com/750x500/891/000/?text=25+-+750x500' },
{ sdMasonryWidth: 1182, sdMasonryHeight: 1000, id: 26, url: 'https://via.placeholder.com/1182x1000/464/000/?text=26+-+1182x1000' },
{ sdMasonryWidth: 1396, sdMasonryHeight: 2092, id: 27, url: 'https://via.placeholder.com/1396x2092/d5d/000/?text=27+-+1396x2092' },
{ sdMasonryWidth: 666, sdMasonryHeight: 1000, id: 28, url: 'https://via.placeholder.com/666x1000/989/000/?text=28+-+666x1000' },
{ sdMasonryWidth: 931, sdMasonryHeight: 698, id: 29, url: 'https://via.placeholder.com/931x698/6c6/000/?text=29+-+931x698' },
{ sdMasonryWidth: 1023, sdMasonryHeight: 1535, id: 30, url: 'https://via.placeholder.com/1023x1535/122/000/?text=30+-+1023x1535' },
];
private page2 = [
{ sdMasonryWidth: 855, sdMasonryHeight: 1282, id: 31, url: 'https://via.placeholder.com/855x1282/89e/000/?text=31+-+855x1282' },
{ sdMasonryWidth: 967, sdMasonryHeight: 1450, id: 32, url: 'https://via.placeholder.com/967x1450/752/000/?text=32+-+967x1450' },
{ sdMasonryWidth: 1680, sdMasonryHeight: 1120, id: 33, url: 'https://via.placeholder.com/1680x1120/871/000/?text=33+-+1680x1120' },
{ sdMasonryWidth: 1060, sdMasonryHeight: 708, id: 34, url: 'https://via.placeholder.com/1060x708/211/000/?text=34+-+1060x708' },
{ sdMasonryWidth: 1500, sdMasonryHeight: 1000, id: 35, url: 'https://via.placeholder.com/1500x1000/6a8/000/?text=35+-+1500x1000' },
{ sdMasonryWidth: 1015, sdMasonryHeight: 1522, id: 36, url: 'https://via.placeholder.com/1015x1522/137/000/?text=36+-+1015x1522' },
{ sdMasonryWidth: 1252, sdMasonryHeight: 765, id: 37, url: 'https://via.placeholder.com/1252x765/1db/000/?text=37+-+1252x765' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 38, url: 'https://via.placeholder.com/1000x1500/ec6/000/?text=38+-+1000x1500' },
{ sdMasonryWidth: 864, sdMasonryHeight: 1296, id: 39, url: 'https://via.placeholder.com/864x1296/a9b/000/?text=39+-+864x1296' },
{ sdMasonryWidth: 760, sdMasonryHeight: 760, id: 40, url: 'https://via.placeholder.com/760x760/77e/000/?text=40+-+760x760' },
{ sdMasonryWidth: 1500, sdMasonryHeight: 1000, id: 41, url: 'https://via.placeholder.com/1500x1000/686/000/?text=41+-+1500x1000' },
{ sdMasonryWidth: 966, sdMasonryHeight: 1296, id: 42, url: 'https://via.placeholder.com/966x1296/987/000/?text=42+-+966x1296' },
{ sdMasonryWidth: 1152, sdMasonryHeight: 768, id: 43, url: 'https://via.placeholder.com/1152x768/214/000/?text=43+-+1152x768' },
{ sdMasonryWidth: 1004, sdMasonryHeight: 1385, id: 44, url: 'https://via.placeholder.com/1004x1385/c6c/000/?text=44+-+1004x1385' },
{ sdMasonryWidth: 1016, sdMasonryHeight: 677, id: 45, url: 'https://via.placeholder.com/1016x677/776/000/?text=45+-+1016x677' },
{ sdMasonryWidth: 864, sdMasonryHeight: 1296, id: 46, url: 'https://via.placeholder.com/864x1296/2ec/000/?text=46+-+864x1296' },
{ sdMasonryWidth: 526, sdMasonryHeight: 750, id: 47, url: 'https://via.placeholder.com/526x750/565/000/?text=47+-+526x750' },
{ sdMasonryWidth: 600, sdMasonryHeight: 750, id: 48, url: 'https://via.placeholder.com/600x750/62e/000/?text=48+-+600x750' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 49, url: 'https://via.placeholder.com/1000x1500/7a7/000/?text=49+-+1000x1500' },
{ sdMasonryWidth: 1040, sdMasonryHeight: 1560, id: 50, url: 'https://via.placeholder.com/1040x1560/d16/000/?text=50+-+1040x1560' },
{ sdMasonryWidth: 1040, sdMasonryHeight: 1560, id: 51, url: 'https://via.placeholder.com/1040x1560/e4a/000/?text=51+-+1040x1560' },
{ sdMasonryWidth: 1040, sdMasonryHeight: 1560, id: 52, url: 'https://via.placeholder.com/1040x1560/6b4/000/?text=52+-+1040x1560' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1500, id: 53, url: 'https://via.placeholder.com/1000x1500/81e/000/?text=53+-+1000x1500' },
{ sdMasonryWidth: 1120, sdMasonryHeight: 1680, id: 54, url: 'https://via.placeholder.com/1120x1680/7de/000/?text=54+-+1120x1680' },
{ sdMasonryWidth: 722, sdMasonryHeight: 1084, id: 55, url: 'https://via.placeholder.com/722x1084/d96/000/?text=55+-+722x1084' },
{ sdMasonryWidth: 1000, sdMasonryHeight: 1250, id: 56, url: 'https://via.placeholder.com/1000x1250/e5a/000/?text=56+-+1000x1250' },
{ sdMasonryWidth: 945, sdMasonryHeight: 1417, id: 57, url: 'https://via.placeholder.com/945x1417/92b/000/?text=57+-+945x1417' },
{ sdMasonryWidth: 912, sdMasonryHeight: 1283, id: 58, url: 'https://via.placeholder.com/912x1283/63c/000/?text=58+-+912x1283' },
{ sdMasonryWidth: 1197, sdMasonryHeight: 1820, id: 59, url: 'https://via.placeholder.com/1197x1820/e6a/000/?text=59+-+1197x1820' },
{ sdMasonryWidth: 987, sdMasonryHeight: 1480, id: 60, url: 'https://via.placeholder.com/987x1480/6de/000/?text=60+-+987x1480' },
];
/**
* Populate this.data
*/
constructor() {
this.data = this.page1; /** Loading in Page 1 */
setTimeout(() => {
this.data = this.data.concat(this.page2); /** Loading in Page 2 */
}, 5000);
}
}
/**
* Your own child component for each sd-masonry cell
*/
@Component({
selector: 'app-item-image',
template: `<img [src]="item.url" [alt]="item.id">`,
styles: ['img { width: 100%; height: 100%; object-fit: cover; border-radius: 5px;}'],
})
export class ItemComponent {
@Input() item: {sdMasonryWidth: number, sdMasonryHeight: number, id?: number, url?: string};
}
Update app.module.ts
to include SdMasonryModule
and ItemComponent
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent, ItemComponent } from './app.component';
import { SdMasonryModule } from 'sd-masonry';
@NgModule({
declarations: [
AppComponent,
ItemComponent
],
entryComponents: [ItemComponent],
imports: [
BrowserModule,
SdMasonryModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Why not try adding in your own animation to the mix? Just edit your ItemComponent
like so :
import {animate, state, style, transition, trigger} from '@angular/animations';
// ...
@Component({
selector: 'app-item-image',
template: `<img [@scaleAnimation]="state" [src]="item.url" [alt]="item.masonryStyle">`,
styles: ['img { width: 100%; height: 100%; object-fit: cover;}'],
animations: [trigger('scaleAnimation', [
state('beginning', style({transform: 'scale(0)', opacity: 0})),
state('end', style({transform: 'scale(1)', opacity: 1})),
transition('beginning => end', [animate('300ms')]),
])]
})
export class ItemComponent implements OnInit{
@Input() item: {adMasonryWidth: number, sdMasonryHeight: number, id?: number, url?: string};
public state = 'beginning';
ngOnInit(): void {
setTimeout(() => {
this.state = 'end';
}, Math.random() * 600);
}
}
Remember to update app.module.ts
to import the BrowserAnimationsModule
imports: [
BrowserModule,
SdMasonryModule,
BrowserAnimationsModule
],
If you chose to include an id
property, you'll also be able individually update an item's property, add a new item into the middle of the stack, and delete an item. Try updating your app.component.ts
to the following
private newItem = { sdMasonryWidth: 1368, sdMasonryHeight: 2739, id: 61, url: 'https://via.placeholder.com/966x1296/000/fff/?text=NEW+ITEM' };
/**
* Populate this.data
*/
constructor() {
const delay = 2000;
let i = 2;
/** Loading in Page 1 */
this.data = this.page1;
/** Change an item's properties (and dimensions) - changing background to red */
setTimeout(() => {
this.data = this.data.map(item => {
if (item.id === 23) {
item.url = 'https://via.placeholder.com/146x430/f00/000/?text=23+-+146x430';
item.sdMasonryWidth = 146;
item.sdMasonryHeight = 430;
}
return item;
});
}, delay * i++);
/** Add a new item randomly into the stack */
setTimeout(() => {
this.data.splice(12, 0, this.newItem);
this.data = [].concat(this.data); /** sd-masonry uses ngOnChange(), so mutated data won't trigger that lifecycle event π */
}, delay * i++);
/** Loading in Page 2 */
setTimeout(() => {
this.data = this.data.concat(this.page2);
}, delay * i++);
/** Now... Let's remove the item we just added */
setTimeout(() => {
this.data = this.data.filter((item, index) => index !== 12);
}, delay * i++);
}
If you're getting Ivy
errors, you may need to update tsconfig.json
or tsconfig.app.json
with
"angularCompilerOptions": {
"enableIvy": false
}
To get more help on the Angular CLI use ng help
or go check out the Angular CLI README.