Guide to creating a rising or waving effect for text using CSS or JavaScript without the need for animation

Is there a way to style dynamic text like this using HTML?

https://i.sstatic.net/NQ9Cs.jpg I'm open to using CSS or Javascript, as long as it does not involve animation.

Answer №1

Using CSS transforms to create a faux effect:

Math.easeInOutSine = function (t, b, c, d) {
  return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
};
var easing = Math.easeInOutSine,
    $target = $('#target'),
    position = $target.position(),
    width = $target.width(),
    height = 100;
$target.html($target.text().split('').map(
  function(char){
    return '<span>'+char+'</span>';
  }
).join(''));
var $chars = $target.children();
$chars.each(function(){
  var $char = $(this),
      w = $char.width(),
      l = $char.position().left,
      offset = easing(l, 0, height, width),
      skew = offset - easing(w + l, 0, height, width),
      angle = Math.atan(skew/w) * 180 / Math.PI;
  offset = -offset;
  $char.css({
    transform: 'translateY('+offset+'px) skewY('+angle+'deg)'
  });
});
$target.css({
  paddingTop: height
});
#target {
  white-space:pre;
  display:inline-block;
}
#target span {
  position:relative;
  display:inline-block;
  transform-origin:left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div><div id="target">This is a test of the emergency broadcast system.</div></div>

http://codepen.io/ouroborus/pen/mmbzje

I refer to it as faking because the letters are not actually curved. Instead, they are broken down into linear segments that approximate a curve based on the width of each character. This approximation becomes more evident at larger font sizes.

Answer №2

To obtain glyph data for any text, you can utilize the opentypejs library. Subsequently, convert this data into an SVG path. By employing the warpjs library, custom functions can be used to distort the SVG into the desired format.

If you wish to explore more examples, visit the link provided below.


An illustration showing a waving text in SVG is demonstrated below.

opentype.load(
  'https://fonts.gstatic.com/s/montserrat/v13/JTURjIg1_i6t8kCHKm45_c5H3gnD-A.woff',
  (err, font) => {
    const glyphs = font.stringToGlyphs('DEVELOPER');
    const { width, yValues, svgPath } = glyphs.reduce(
      (prev, glyph) => {
        const d = glyph.path.commands.map(({ type, x1, y1, x, y }) => {
          switch (type) {
            case 'M':
            case 'L':
              return `${type} ${x + prev.width} ${y}`;
            case 'Q':
              return `${type} ${x1 + prev.width} ${y1} ${x +
                prev.width} ${y}`;
            default:
              return type;
          }
        });
        return {
          svgPath: `${prev.svgPath}<path d="${d.join('')}" />`,
          width: prev.width + glyph.advanceWidth,
          yValues:
            typeof glyph.yMax !== 'number'
              ? prev.yValues
              : [...prev.yValues, glyph.yMax, glyph.yMin],
        };
      },
      { svgPath: '', width: 0, yValues: [] },
    );
    const yMax = Math.max(...yValues);
    const yMin = Math.min(...yValues);
    const height = yMax - yMin;

    const period = width;
    const amplitude = height / 2;

    const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
    svg.setAttribute('viewBox', `0 0 ${width} ${height + amplitude * 2}`);
    svg.innerHTML = `<g transform="translate(0,${yMax +
      amplitude}) scale(1 -1)">${svgPath}</g>`;

    const warp = new Warp(svg);
    warp.transform(([x, y]) => [
      x,
      y + amplitude * Math.sin((x / period) * Math.PI * 2),
    ]);
    
    document.getElementById('example').appendChild(svg);
  },
);
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e4938596948e97a4d5cad4cadc">[email protected]</a>/dist/warp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/opentype.js/1.1.0/opentype.min.js"></script>
<div id="example" />

Answer №3

After taking inspiration from @Mike Diglio's solution, we can improve upon it.

var letterContainer = document.querySelector(".letters");

var letters = letterContainer.innerText;

letters = letters.split("");

letterContainer.innerText = "";

var offset = 0;

letters.forEach(function(letter, i){
  var wrap = document.createElement("span");
  wrap.innerText = letter;
  wrap.style.position = "relative";
  wrap.style.bottom = i+"px";
  wrap.style.transform = "rotate("+-i+"deg)";
  letterContainer.appendChild(wrap);
})
<div class="letters">HELLO WORLD</div>

You now just have to devise a function that handles the mapping for you.

Answer №4

It is possible to achieve the desired outcome in a variety of ways.

  1. One approach involves wrapping each letter of the word in a span tag and assigning unique classes to each span. While this method may not be visually appealing in the markup, it can present challenges with responsiveness depending on the word length and placement. (You can view an example HERE)

.container {
  margin: 50px;
  font-size: 20px;
  letter-spacing: -2px;
}
span {
  display: inline-block;
}
.h {
  position: relative;
  transform: rotate(-2deg);
}
.e {
  position: relative;
  bottom: 2px;
  transform: rotate(-5deg);
}
.l {
  position: relative;
  bottom: 5px;
  transform: rotate(-7deg);
}
.el {
  position: relative;
  bottom: 7px;
  transform: rotate(-9deg);
}
.o {
  position: relative;
  bottom: 10px;
  transform: rotate(-11deg);
}
<div class="container">
  <span class="h">H</span>
  <span class="e">E</span>
  <span class="l">L</span>
  <span class="el">L</span>
  <span class="o">O</span>
</div>

  1. Alternatively, following @Ouroborus's suggestion, utilizing an SVG element to create a path for text movement along a curve effect can be effective (Further information available HERE).
  2. Another option includes employing a JS library like Lettering.JS. Although I have not personally used this tool, it comes highly recommended based on feedback from Stack Overflow users and appears to offer robust functionality upon initial review.

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

Installing Yarn causes the download of an unconventional directory

Currently, I am facing an issue while trying to install yarn on my Macbook Pro (2017). The installation process seems to be downloading a folder called /react-praktis/ instead of completing successfully. Below is a screenshot for reference: https://i.stac ...

Transfer the textbox value from page-1 to page-2 seamlessly with the help of AngularJS UI-routing

I'm attempting to transfer the textbox value from Page-1 to Page-2 using the code below, but I am encountering difficulties. Page-1.html <div > <div style="height: 400px"> <h2>Partial view-1</h2> <p> ...

Is it possible to use jQuery to search an unordered list by simply pressing a key on the keyboard?

I have a list of countries displayed as an unordered list inside a table column. I would like to implement keyboard search functionality for quicker navigation within the list. <ul class="country-search"> <li id="1">Country 1</li> ...

How come my menu is not showing up in the same format as the code?

I'm struggling to make the menu align with the text logo. Every time I try, the menu ends up below the logo instead of beside it. Can anyone provide some assistance? nav{ width: 1330px; margin: 0px auto; } nav ul li{ text-transform: up ...

A guide on invoking a JavaScript function within a dropdown menu based on selection instead of change event

I need to automatically trigger a JavaScript function based on the value pulled from the dropdown options that are populated by a database. Currently, the JavaScript function only runs when I manually select an option on the front-end. Below is my code. I ...

Retrieve JSON data generated within a JavaScript-powered webpage

My issue involves two HTML pages: 1) The first HTML Page (page1.html): <html lang="en"> <head> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js" type="text/javascript"></script> <script type="text/ ...

Making AngularJS 'PUT' requests: The process of submitting only the data in a form

I am facing an issue while updating user data in Angular. When I send a 'PUT' request, the entire user $scope is being sent instead of only the fields visible on the form. To retrieve and update the data, I am using a Factory. Below is my edit f ...

Encountering 404 errors when reloading routes on an Angular Azure static web app

After deploying my Angular app on Azure static web app, I encountered an error. Whenever I try to redirect to certain routes, it returns a 404 error. However, if I navigate from one route to another within the app, everything works fine. I have attempted t ...

When the mouse is moved to the UL LI list, the border color of the Top Hover is reverted back to default

Can you assist me with this issue? I need to modify the code below so that the border color remains blue while a user selects an item from the UL LI list. Here is an image showing the current problem: https://i.sstatic.net/DS7hO.png And here is a pictu ...

JavaScript providing inaccurate height measurement for an element

Upon loading the page, I am trying to store the height of an element. To achieve this, I have utilized the jQuery "ready" function to establish a callback: var h_top; var h_what; var h_nav; $(".people").ready(function() { h_top = $(".to ...

Creating borders around Material UI grid items can be achieved by applying a border style to the

import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Paper from '@material-ui/core/Paper'; import Grid from '@material-ui/core/Grid'; const useStyles = makeStyles((theme) => ({ ...

Update the CSS classes exclusively for the specified ID

Attempting to modify and override certain classes within the complex control (dxList) of DevExtreme has been successful thus far. .dx-loadpanel-content { transform: translate(0px, 0px) !important; margin: auto !important; left: 0px !important; ...

Enhance PHP search functionality by showcasing hidden auto-complete suggestions in real-time

I am attempting to showcase unlisted search results that can be clicked on to redirect the user to a specific HTML page based on data retrieved from a MySQL database. My development environment is PhoneGap. Thus far, I have successfully set up a basic PH ...

Failing to retrieve the file instance upon completing the upload process to cloudinary using nestjs

I am attempting to retrieve the secure file URL provided by Cloudinary after successfully uploading the asset to their servers. Although I can upload the file to Cloudinary, when I try to view the response using console.log(res), I unfortunately receive &a ...

Angular often uses the JavaScript pattern for development

After completing an AngularJS tutorial on http://www.tutorialspoint.com/angularjs/angularjs_services.htm, I found myself puzzled by the method used in the CalcService service. It seemed unclear whether Angular was using revealing prototype or a different ...

The router.push function does not properly redirect to the specified path; instead, it just reloads the current page

I am a newcomer to NextJS and facing a challenge where I need to transfer data from the current page to another page. However, instead of loading the defined path in router.push as pathname: "/booking/checkout/", it loads the current page. I wan ...

Should I deploy the Angular2 demo app within Rails or as its own standalone application?

Currently diving into the world of Angular2 and completing both the quickstart and heroes tutorial. Each time I start these applications, I use the "npm start" command. On top of that, I've developed a Ruby on Rails backend application alongside an A ...

How can I reduce the burden of dependencies on users with a pre-built NPM package?

I recently took over maintenance of an NPM package. It has a unique setup where the main file is located in the 'dist/' directory and it's built using webpack (via 'npm run build'). While this works for us, installing this package ...

Employing parseFloat() and parseInt() functions together with regular expressions in JavaScript for converting a Comma Separated Values (CSV

I've been working on converting a CSV file to a local 2D array and I'm curious if there's a more efficient method of changing strings to floats/int rather than relying on regex paired with parseFloat() / parseInt. Any bright ideas or sugges ...

Issue encountered when attempting to modify the directive when the drop-down list is changed in AngularJS

Experiencing issues updating the directive when the drop down list is changed using AngularJS. Below is my application code: HTML Code <div ng-app="myApp" ng-controller="MyCtrl"> <select ng-model="opt" ng-options="font.title for font in font ...