Learn How To Create Custom Placeholder Images for Your Web App in No Time

Hitesh Mandav
Geek Culture
Published in
6 min readJun 11, 2022

--

Header image

Image placeholders are very essential for any application and while a lot of dummy APIs are available for the same. Many times I find myself not wanting to rely on these may be for the purpose of the stability, cost or traffic etc. So sometimes we end up using same generic placeholders for all profiles which i find boring. So i have put up this small tutorial that will help in quickly setting up custom placeholder images for the user.

Here we will be using canvas to create a placeholder profile Picture with the name’s initials and a gradient header image placeholder with the same color in an Angular application if the images are not present in the database.

Here we will cover the following topics:

  • Overview
  • Generate Random Color
  • Generate Inverted Color
  • Generate Header Styles
  • Generate Profile Image

Overview

The Sample API response that I will be using within this demo.

// Sample JSON respponse for user Data
{
"id": 1,
"firstName": "Den",
"lastName": "Draysay",
"role": "Professor",
"profileImg": null,
"headerImg": null
}
// user interface defined for type
export interface User {
firstName: string;
lastName: string;
role?: string;
profileImg?: string;
headerImg?: string;
color?: string;
}

Create an angular component “user-profile”, services “color” and “user” and a pipe “profile-image”.

In the user profile Component, we can create a variable user profile of the type User to store the response from the API onInit.

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { User } from 'src/app/models/user';
import { UserService } from 'src/app/services/user.service';
@Component({
selector: 'at-user-profile',
templateUrl: './user-profile.component.html',
styleUrls: ['./user-profile.component.scss'],
})
export class UserProfileComponent implements OnInit {
public userProfile!: User;
public userId!: string;
constructor(
private readonly userService: UserService,
private readonly route: ActivatedRoute,
) { }
public async ngOnInit(): Promise<void> {
await this.route.params.subscribe(params =>
this.userId = params['userId']
)
await this.fetchUserData();
}
private fetchUserData(): void {
this.userService.fetchUserData(this.userId).
subscribe((data: User)=> {
this.userProfile = data;
});
}
}

The user service will be used to make the HTTP call.

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../models/user';
@Injectable({
providedIn: 'root'
})
export class UserService {
private readonly baseUrl = `http://localhost:3000`;
constructor(
private readonly http: HttpClient
) { }
public fetchUserData(userId: string): Observable<User> {
return this.http.get<User>(`${this.baseUrl}/users/${userId}`);
}
}

Generate Random Color

Our first step is to get the random color used in our placeholder images. So, we will create a method in the service that will return us valid hex codes.

public generateRandomColorCode(): string {
const hex = Math.floor(Math.random()*16777215).toString(16);
const randomColorCode = hex.toString().length < 6 ?
this.generateRandomColorCode() : `#${hex}`;
return randomColorCode ;
}

now we can use this method to set a color for the user profile that was fetched in the user-profile component.

in the fetchUserData. subscribe we can call this service after injecting the color service in the constructor.

If we want to maintain the same color for the user for any future logins we can save the randomly generated color to the DB along with the user details or if we need a new color every time the user visits we can call this method.

// to use a new color every time
this.userProfile.color = this.color.generateRandomColorCode();
// to use same color every time
this.userProfile.color = this.userProfile.color ?
this.userProfile.color :
this.color.generateRandomColorCode();

Generate Inverted Color

We also need a method that can give us an black or white color based on the hex color we have to print the initials on the profile picture or create a gradient for the header.

we can create this method in the color service.

public getInvertedColor(hex: string): string{
if (hex.indexOf('#') === 0) {
hex = hex.slice(1);
}
const r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16);
return (r * 0.299 + g * 0.587 + b * 0.114) > 186 ?
'#000000' :
'#FFFFFF';
}

Note: This method can be improved to give a wide vaierty of colors that are inversly propostional to the hex colr to generate a gradient and onlyy out put black and white for profile picture font conditionally using a flag. click here to get detailed info.

Generate Header Styles

Now we can create a method in the color service or create a custom pipe whichever is preferable to your use case but the base logic is the same, we need a method that will give us CSS styles based on the user profile.

So consideration is the user might have already uploaded a header image so in those cases, we have to show that image and if the user has no header image we will generate a linear gradient based on the random color that we have generated for the user.

public getHeaderStyles(user: User): any {
let headerBkg: any;
if (user.profile.headerImg) {
headerBkg = {
'background-image': `url('${user.headerImg}')`,
'background-size': 'cover',
}
} else {
const secondaryColor = this.getInvertedColor(user.profile.color || '');
headerBkg = {
'background': `linear-gradient(${user.profile.color}, ${secondaryColor})`,
}
}
return headerBkg;
}

user-profile.component.html

<div
class="user-profile-cover"
[ngStyle]= "headerBkg"
></div>

user-profile.component.scss

.user-profile{
&-cover{
height: 20%;
}
}

user-profile.component.ts

create a variable

public headerBkg!: any;

inside fetchUserData.subscribe after assigning the random color to the userProfile.color property, assign the CSS styles to the variable created

this.headerBkg = this.color.getHeaderStyles(this.userProfile);

now you should have a random color header placeholder if there is no header image or an image if the header image is provided.

With Header Image
If the header image is present.
No header image
if no Header image

Generate Profile Image

Now to the interesting part, generate a sample profile pic if not provided by the user.

I’m going to use a custom pipe profile image.

profile-image.pipe.ts

import { Pipe, PipeTransform } from '@angular/core';
import { User } from 'src/app/models/user';
import { ColorService } from '../util/color.service';
@Pipe({
name: 'profileImage'
})
export class ProfileImagePipe implements PipeTransform {constructor(private readonly color: ColorService){}transform(user: User): string {
if(user.profile.profileImg){
return user.profile.profileImg;
}
const canvas = document.createElement('canvas');
canvas.style.display = 'none';
canvas.width = 50;
canvas.height = 50;
document.body.appendChild(canvas);
const context = canvas.getContext('2d');
context!.fillStyle = user?.profile.color || '';
context!.beginPath();
context!.arc(canvas.width/2, canvas.height/2, canvas.height/2, 0, Math.PI * 2);
context!.fill();
context!.font = '20px Arial';
context!.fillStyle = this.color.getInvertedColor(user.profile.color || '');
const initials = this.getInitials(
user.profile.firstName,
user.profile.lastName);
context?.fillText(initials, canvas.width/4.5, canvas.height/1.5);const data = canvas.toDataURL();
document.body.removeChild(canvas);
return data;
}
private getInitials(firstName: any, lastName: any): string {
return firstName.charAt(0).toUpperCase() + lastName.charAt(0).toUpperCase();
}
}

Note: now we can use this pipe directly in the html on a src attribute of img or create a variable in the user profile component and use the pipe transform method to store the src value in that variable.

user-profile.component.html

<!--Header-->
<div
class="user-profile-cover"
[ngStyle]= "headerBkg"
></div>
<div class="user-profile-info">
<!--profile Image-->
<div class="user-profile-info-img">
<img
class="user-profile-info-img__img"
[src]="userProfile | profileImage"
>
</div>
<h2 class="user-profile-info__name">{{fullName}}</h2>
<h5 class="user-profile-info__role">{{userProfile.role}}</h5>
</div>

user-profile.component.scss

.user-profile {
&-cover {
height: 20%;
}
&-info {
display: flex;
flex-direction: column;
align-items: center;
&-img {
display: flex;
justify-content: center;
&__img {
margin-top: -20px;
border: #fff 2px solid;
border-radius: 50%;
width: 50px;
height: 50px;
}
}
&__name{
margin: 0;
}
&__role{
margin: 0;
}
}
}

after all this coding is done you should be done and should have a sample placeholder for the header image and profile image if either is not provided by the user and in case the user provides the image then the image should be displayed instead of the placeholder.

If the user provides images.
If the user does not provide images

Thank you for the read do reach out to me in the comments if you have any questions or suggestions.

--

--