Adjust the size of the cursor

Currently, I am in the process of creating a drawing application akin to Paint or Sketchpad. One issue I have encountered is the inability to resize the cursor based on the line width of the pencil I am using. The workaround I have discovered involves using a custom cursor, where the standard cursor is replaced with an image that can be resized. However, I am unsure whether I need to implement a function to resize the image or if I can directly adjust the image size using SCSS (CSS).

Here is the code I have implemented thus far:

private setCursor(cursorType: DrawingCursor): void {
this.Canvas.setAttribute("style", "cursor:url(" + cursorType + "), auto;");}

The cursorType variable contains the URL of the custom cursor image.

I am developing this functionality within an Angular 8 project using Typescript.

Any guidance on this matter would be greatly appreciated! Thank you!

Answer №1

If you want to dynamically apply CSS classes in Angular, you can use NgClass binding. This feature allows Angular to handle the application of CSS classes, eliminating the need to set styles manually.

To implement this approach, you first need to define the CSS classes for the canvas and the different cursor sizes. Here's an example:

.myCanvas {
  width: 400px;
  height: 400px;
  background-color: green;
}

.brush18px {
  cursor: url('brush18px.png'), auto;
}

.brush24px {
  cursor: url('brush24px.png'), auto;
}

.brush36px {
  cursor: url('brush36px.png'), auto;
}

Next, in your component, define a property to provide the classes for the canvas and another property for the brush size. Here's a sample component setup:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';
  @Input() cursorSize = '18';
  get canvasClasses() {
    return {
      myCanvas: true,
      brush18px: this.cursorSize === '18',
      brush24px: this.cursorSize === '24',
      brush36px: this.cursorSize === '36',
    };
  };
}

The canvasClasses property generates an object with CSS class names and their application status (true or false) based on the cursor size. By using a getter property, the values update automatically when the size changes.

You can then bind this in the template to have the cursor update dynamically based on the cursorSize value. Here's how the template code looks like:

<canvas [ngClass]="canvasClasses"></canvas>
<br/>
<select [(ngModel)]="cursorSize">
  <option>18</option>
  <option>24</option>
  <option>36</option>
</select>

I have provided a working code sample on StackBlitz for you to see this functionality in action.

Answer №2

Considering an alternative approach derived from the feedback provided in the initial response. This workaround involves concealing the actual cursor and utilizing an image to simulate its movement. The inspiration for this solution stemmed from the second response in this thread on resizing a cursor image.

I integrated this workaround in Angular by creating a directive that can be applied to the canvas element within the template. The directive requires the specification of the image to be displayed as the cursor, along with an additional parameter for setting the image size. It is advisable to utilize an SVG image for optimal resizing, although conventional image formats can also be used.

Below is the implementation of the Directive for an SVG image:

@Directive({
  selector: '[svgCursor]'
})
export class SvgCursorDirective {
  private cursorSizeValue: number = 16;
  @Input('svgCursor') public svgImage: SVGSVGElement;
  @Input() public set cursorSize(cursorSize: number) {
    this.cursorSizeValue = cursorSize;
    this.updateCursorSize();
  }

  constructor(el: ElementRef) {
    el.nativeElement.style.cursor = 'none'; // hides the browser cursor
  }

  @HostListener('mouseenter') onMouseEnter() {
    // displays the image only when the mouse enters the element
    this.svgImage.style.visibility = 'visible';
  }

  @HostListener('mousemove', ['$event']) onMouseMove(e: MouseEvent) {
    // positions the image at the mouse cursor
    this.svgImage.style.left = e.clientX.toString();
    this.svgImage.style.top = (e.clientY - this.cursorSizeValue).toString();
  }

  @HostListener('mouseleave') onMouseLeave() {
    // hides the image when the mouse leaves the element
    this.svgImage.style.visibility = 'hidden';
  }

  private updateCursorSize() {
    if (this.svgImage != null) {
      this.svgImage.style.width = this.cursorSizeValue.toString();
      this.svgImage.style.height = this.cursorSizeValue.toString();
    }
  }
}

Once the directive is in place, it can be utilized within a component template as demonstrated below:

<svg #cursorImage class="cursor" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M7 14c-1.66 0-3 1.34-3 3 0 1.31-1.16 2-2 2 .92 1.22 2.49 2 4 2 2.21 0 4-1.79 4-4 0-1.66-1.34-3-3-3zm13.71-9.37l-1.34-1.34c-.39-.39-1.02-.39-1.41 0L9 12.25 11.75 15l8.96-8.96c.39-.39.39-1.02 0-1.41z"/></svg>
<canvas class="myCanvas" [svgCursor]="cursorImage" [cursorSize]="cursorSize"></canvas>

As illustrated, a template reference variable must be assigned to the image in order to pass it as a parameter to the svgCursor directive.

Additionally, to ensure proper functionality, appropriate CSS styles need to be set for the image to eliminate unnecessary elements. The image is initially hidden and only becomes visible upon entering the canvas area.

Below are the CSS styles employed:

.myCanvas {
  width: 400px;
  height: 400px;
  background-color: lightgreen;
}

.cursor {
  position: absolute;
  cursor: none;
  pointer-events: none;
  visibility: hidden;
}

A functional illustration of this implementation is available in a live StackBlitz demo for better understanding.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

A minimalist web page appearing as blank in Internet Explorer

After testing my simple web page in various browsers, I discovered that it only showed a blank white page in Internet Explorer. I combed through my CSS code and identified background = rgba(some stuff) as the unsupported line by IE. However, this particula ...

`Responsive Site with Centered Image Overlay`

I've been attempting to center a small image within a larger one, both of which are contained within a Bootstrap Column. <div className="row justify-content-center my-5 "> <div className="col-xs-10 col-sm-6 my-auto mx-auto" ...

Is there a way to retrieve the number of swipe up interactions from Instagram story insights using the graph API

Is there a way to retrieve the swipe up count displayed in Instagram insights? Since Facebook does not provide this data through their Graph API, how can I access it? I have already tried scraping without success and I am looking for a solution using eith ...

The operation failed because the property 'dasherize' is inaccessible on an undefined object

While attempting to execute the following command: ng generate component <component-name> An error occurred saying: Error: Cannot read property 'dasherize' of undefined Cannot read property 'dasherize' of undefined The confi ...

Typescript is encountering errors indicating that it is unable to locate modules for imported assets, such as images

Having trouble with TS not recognizing image imports. Although the site runs fine, TypeScript seems to have an issue identifying them: import React, { Component } from 'react'; import SlackIcon from './assets/social/slack-icon-thumb.png&apos ...

Having trouble with sending values to Angular 7 components' HTML pages

Struggling with a simple task and encountering an error: Code snippet below: app.component.html <div class="col-md-{{myvalue}}">stuff here</div> app.component.ts myvalue: string; ngOnInit() { this.myvalue('6'); } Seeing th ...

I'm new to Angular and I'm wondering how to close the panel by clicking on the 'x' button and also by clicking on the screen. Can anyone help me with this

Below is the HTML code I use for my button: <button class="btn btn-outlined " ng-click="vm.showCommentBox1()">Notify All</button> <div class="comment-box custom saveAll" ng-if=""><div class="panel panel-default"> ...

Having issues with the CSS block in my Jekyll dropdown navbar

I have been exploring resources from both w3schools and another website to create a navigation bar in Jekyll using frontmatter. However, I am running into issues with the block property in CSS. Most of the navbar is functioning properly, except for the dro ...

Retrieve the attributes associated with a feature layer to display in a Pop-up Template using ArcGIS Javascript

Is there a way to retrieve all attributes (fields) from a feature layer for a PopupTemplate without explicitly listing them in the fieldInfos object when coding in Angular? .ts const template = { title: "{NAME} in {COUNTY}", cont ...

Creating a multi-column layout for list items using Bootstrap. Unusual behavior observed (see demo in JSBIN

I am struggling to display items in multiple columns without any strange formatting issues. Can someone help me out? Check out the JSBin for reference: http://jsbin.com/huzurejunu/1/edit?html,css,output https://i.sstatic.net/DxJpW.png <div class="ro ...

What could be causing this slider to malfunction in Firefox?

I have recently developed a slider on this page: While the slider functions smoothly in Chrome, I am facing compatibility issues with Firefox. Can anyone shed some light on why this might be happening? Here is the HTML, CSS, and JS code used for the slid ...

What is the best way to seamlessly integrate an image icon into a sentence, so it appears as a natural part of the text

I'm struggling to insert an (i) information icon before the phrase "Learn more" in the sentence: "Click on the 'Learn more' buttons for additional information." The icon keeps overlapping with the words "Learn more." Could someone provide s ...

Distribute the elements evenly between two separate divs

Hello everyone! I have a unique challenge that I hope someone can help me with. I have a list of links to various tests, but different sections require these links to be displayed in different ways. One section needs them spread over two columns, while an ...

The script ceased functioning immediately following the inclusion of a case-insensitive search feature and interactive images

In the process of creating my inaugural project featuring a collection of images, I wanted to include a filter/search bar at the top. This bar would dynamically filter the displayed pictures based on user input. For example, typing "Aatrox" into the search ...

A miniature triangle-shaped bubble crafted with 1px of CSS3 styling

Currently, I am experimenting with CSS3 and creating bubble shapes. However, I have encountered an issue. I am trying to create a triangle with a 1px border, but the result is a thicker triangle than desired. You can view my code on this fiddle : FIDDLE ...

Leverage classes within components for effective dependency injection

Currently, I am working with Angular and have set up 1 component, 1 class, and 1 service in my project. The service is defined as an @Injectable class and properly configured in app.module.ts. @Injectable() export class MyService { My class receives the ...

Spin a Material UI LinearProgress

I'm attempting to create a graph chart using Material UI with the LinearProgress component and adding some custom styling. My goal is to rotate it by 90deg. const BorderLinearProgressBottom = withStyles((theme) => ({ root: { height: 50, b ...

What causes the child positioning to break when a CSS-Filter is applied to the parent element?

One of my projects includes a title-screen "animation" where the title is initially centered on a fullscreen page and then shrinks and sticks to the top as you scroll down. I have provided a simplified working example below: $(window).scroll( () => ...

The resize function fails to trigger when it is required

Struggling to get this code working properly. If the window width is greater than 800, I want 6 images with a red background. If the window width is less than 800, I want 4 images with a blue background. I need this functionality to work both on r ...

Guide on Implementing a Function Post-Rendering in Angular 2+

I'm looking to implement some changes in the Service file without modifying the Component.ts or directive file. Here's what I need: 1) I want to add an event listener after the service renders its content (which is generated by a third-party tool ...