2

I am trying to be able to change css properties of individual characters in a string of text based on the proximity to the mouse position.

Codepen: https://codepen.io/NewbCake/pen/qYXvoo

The idea is to take a string of text and wrap each character in a span with an overall class of ‘single-char’ and a unique class.

The original text string is like this:

<p class='sample-text hover-letter'>This sample text turns red, character by character, when you hover over it with your mouse.</p>

Then it is split into individual character like this:

<span class=“single-char char-0”> T</span>
<span class=“single-char char-1”> h</span>
<span class=“single-char char-2”> i</span>
<span class=“single-char char-3”> s</span>

JS

function arrayMe(string) {

  // For all matching elements
  $(string).each(function() {

    // Get contents of string
    var myStr = $(this).html();

    // Split myStr into an array of characters
    myStr = myStr.split("");

    // Build an html string of characters wrapped in  tags with classes
    var myContents = "";
    for (var i = 0, len = myStr.length; i < len; i++) {
        myContents += '<span class="single-char char-' + i + '">' + myStr[i] + '</span>';
    }

    // Replace original string with constructed html string
    $(this).html(myContents);
console.log(i)
});
(function() {

  var mX, mY, distance,
    $distanceSpan_red = $('#distance_blue span'),
    $distanceSpan_blue = $('#distance_red span'),
    $element0 = $('.char-0'),
    $element1 = $('.char-1');
    $element2 = $('.char-2');
    $element3 = $('.char-3');
    $element4 = $('.char-4');

function calculateDistance(elem, mouseX, mouseY) {
return Math.floor(Math.sqrt(Math.pow(mouseX - (elem.offset().left + (elem.width() / 2)), 2) + Math.pow(mouseY - (elem.offset().top + (elem.height() / 2)), 2)));
}

$(document).mousemove(function(e) {
  mX = e.pageX;
  mY = e.pageY;

  distance0 = calculateDistance($element0, mX, mY);
  distance1 = calculateDistance($element1, mX, mY);
  distance2 = calculateDistance($element2, mX, mY);
  distance3 = calculateDistance($element3, mX, mY);
  distance4 = calculateDistance($element4, mX, mY);


  $element0.css({'font-size': distance0 + 'px'});
  $element1.css({'font-size': distance1 + 'px'});
  $element2.css({'font-size': distance2 + 'px'});
  $element3.css({'font-size': distance3 + 'px'});
  $element4.css({'font-size': distance4 + 'px'});
});
})();
}
// Calling arrayMe on page load, on class "sample-text"
$('document').ready(function() {
  var myStringType = $('.sample-text');
  arrayMe(myStringType);
});

What I am struggling with is how to make the code be flexible and work dynamically. Irrespective of the amount of text, it should be able to measure the distance from the mouse position to each letter's unique class then store that as a distance value then map that distance value to a css property value.

Any help will be appreciated!

NewbCake
  • 433
  • 2
  • 7
  • 22

1 Answers1

2

I restructure your code to make a working example. You need to make an array based on your class single-char so then you can loop it and don hard code the quantity of characters.

I comment you calculateDistance() return as the math is a little funky. But with this example you can see how all the characters are affected

  $(document).mousemove(function(e) {
    var mX = e.pageX;
    var mY = e.pageY;
    $('.single-char').each(function(){
      $(this).css({'font-size': calculateDistance(this, mX, mY) + 'px'});
    });                    
  });

You can target all the chars with $('common-class') and then loop them with .each().

Hope this helps :)

function arrayMe(string){
 $(string).each(function() {
  var myStr = $(this).html();
  myStr = myStr.split("");
  var myContents = "";
  for (var i = 0, len = myStr.length; i < len; i++) {
   myContents += '<span class="single-char char-' + i + '">' + myStr[i] + '</span>';
  }
  $(this).html(myContents);
    console.log(i);
 });
}



 function calculateDistance(elem, mouseX, mouseY) {
    // return Math.floor(Math.sqrt(Math.pow(mouseX - ($(elem).offset().left + ($(elem).width() / 2)), 2) + Math.pow(mouseY - ($(elem).offset().top + ($(elem).height() / 2)), 2)));
   return mouseX;
  }


  $(document).mousemove(function(e) {
    var mX = e.pageX;
    var mY = e.pageY;
    $('.single-char').each(function(){
      $(this).css({'font-size': calculateDistance(this, mX, mY) + 'px'});
    });                    
  });


$('document').ready(function() {
 var myStringType = $('.sample-text');
 arrayMe(myStringType);
});
.single-char:hover {
 color:red;
 cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class='sample-text hover-letter'>This sample text turns red, character by character, when you hover over it with your mouse.</p>

This code has been added to help the OP after a follow up comment.

function arrayMe(string){
 $(string).each(function() {
  var myStr = $(this).html();
  myStr = myStr.split("");
  var myContents = "";
  for (var i = 0, len = myStr.length; i < len; i++) {
   myContents += '<span class="single-char char-' + i + '">' + myStr[i] + '</span>';
  }
  $(this).html(myContents);
    console.log(i);
 });
}



 function calculateDistance(elem, mouseX, mouseY) {
    // return Math.floor(Math.sqrt(Math.pow(mouseX - ($(elem).offset().left + ($(elem).width() / 2)), 2) + Math.pow(mouseY - ($(elem).offset().top + ($(elem).height() / 2)), 2)));
   return mouseX;
  }




$('document').ready(function() {
 var myStringType = $('.sample-text');
 arrayMe(myStringType);
  
    $('.single-char').hover(function(e) {
      var charNumber = $(this).attr('class').split('-')[2];
      $('.single-char').each(function(){
       $(this).css({'font-size': 12 + 'px'});
      }); 
    $(this).css({'font-size': 36 + 'px'});           
  });
});
.single-char:hover {
 color:red;
 cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class='sample-text hover-letter'>This sample text turns red, character by character, when you hover over it with your mouse.</p>

This is a second follow up snippet, to demostrate the funkiness of the math.

function arrayMe(string){
 $(string).each(function() {
  var myStr = $(this).html();
  myStr = myStr.split("");
  var myContents = "";
  for (var i = 0, len = myStr.length; i < len; i++) {
   myContents += '<span class="single-char char-' + i + '">' + myStr[i] + '</span>';
  }
  $(this).html(myContents);
    console.log(i);
 });
}



 function calculateDistance(elem, mouseX, mouseY) {
    return Math.floor(Math.sqrt(Math.pow(mouseX - ($(elem).offset().left + ($(elem).width() / 2)), 2) + Math.pow(mouseY - ($(elem).offset().top + ($(elem).height() / 2)), 2)));
  }




$('document').ready(function() {
 var myStringType = $('.sample-text');
 arrayMe(myStringType);
  
    $('.single-char').hover(function(e) {
      var mX = e.pageX;
      var mY = e.pageY;
      var charNumber = $(this).attr('class').split('-')[2];
      $('.single-char').each(function(){
       $(this).css({'font-size': calculateDistance($(this), mX, mY) + 'px'});
      });      
  });
});
.single-char:hover {
 color:red;
 cursor:pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class='sample-text hover-letter'>This sample text turns red, character by character, when you hover over it with your mouse.</p>
Gerardo BLANCO
  • 5,590
  • 1
  • 16
  • 35
  • Thank you for your response @Gerardo BLANCO. After running the code snippet I realised I may not have been clear on the end result. What I am trying to do is that as the mouse moves over the text this causes EACH letter changes rather than the whole text. This would create a sort of gradient from the letters closest to the mouse to those that are farthest. – NewbCake May 05 '18 at 00:22
  • @NewbCake, your question was "What I am struggling with is how to make the code be flexible and work dynamically. Irrespective of the amount of text". I would love to help you but please try to put multiple questions on multiple post. This answer actually solves the original question. If you post another question updating the status and with the new problem, I would love helping you and more people will get to you. – Gerardo BLANCO May 05 '18 at 00:28
  • @NewbCake i add a snippet including somewhat you are looking for. Try working from there. If you get stuck ask an other question and comment the link here. Please note that the preferred way of saying 'thanks' around here is by up-voting good questions and helpful answers, and by accepting the most helpful answer to any question you ask (which also gives you a small boost to your reputation). – Gerardo BLANCO May 05 '18 at 00:43
  • The effect I am going for is seen in the original codepen, where the first four letters change their size depending on the position of the mouse. This is done by each measurement pair is written manually. The flexible solution I was asking about is how to avoid writing all the pairs, and instead have the code automatically measure to all the letters, no matter how many there are. Your provided (updated) answer strays from the original codepen idea and only changes one letter at a time and doesn't change the size of the letter based on the distance from the mouse. – NewbCake May 05 '18 at 00:58
  • @NewbCake i added anothe code snippet to demostrate as i initially stated: "I comment you calculateDistance() return as the math is a little funky". The question doesnt involve the math around how you change the values. It is about how you can change the values no matter how many letter ther are. My solutions have provided that. – Gerardo BLANCO May 05 '18 at 01:07
  • Sort of. I don't quite understand why it only responds to the mouse position when it touches a letter and why it only shrinks. – NewbCake May 05 '18 at 01:42
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/170402/discussion-between-gerardo-blanco-and-newbcake). – Gerardo BLANCO May 05 '18 at 01:47