Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add image handling for waveColor and progressColor #2348

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6899409
enhancement: add image handling for waveColor and progressColor param…
califken Aug 28, 2021
fbb1735
Delete settings.json
califken Aug 28, 2021
25d3b51
update type checks and JSdocs for the set and getFillStyle methods
califken Aug 29, 2021
a62bfd7
update image fill style example HTML CSS and WaveSurfer options
califken Aug 29, 2021
13aa47a
return ctx.createPattern rather than store it in a variable
califken Aug 31, 2021
f94d789
fix CHANGES.md rename section instead of add new section
califken Aug 31, 2021
37fe8a4
update: add setWaveStyle and setProgressStyle methods to wavesurfer.js
califken Sep 5, 2021
d74d8ab
fix: space issues
califken Sep 11, 2021
37c73df
fix: change br elements to ul and li
califken Sep 11, 2021
5f01b66
Merge branch 'master' into image-fill-styles
califken Sep 11, 2021
f4e25f5
fix: add version marker to new methods
califken Sep 12, 2021
ce75654
docs: add changelog entry for pull request #2348
califken Sep 15, 2021
8ea3907
feat: add repeat option for image fill style
califken Sep 24, 2021
bc3134c
fix: undo variable declaration change const to let
califken Sep 24, 2021
9e69788
feat: add canvas and video fill style examples
califken Sep 24, 2021
4fba05a
remove audio file and add attribution for video file
califken Sep 28, 2021
41b74a8
fix: updates to canvas and video examples
califken Oct 3, 2021
54c098e
Merge branch 'master' into image-fill-styles
thijstriemstra Feb 20, 2022
1f567c3
remove trailing space
thijstriemstra Feb 20, 2022
f95472f
use var
thijstriemstra Feb 20, 2022
61416dc
Merge branch 'master' into image-fill-styles
thijstriemstra Jul 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
@@ -1,5 +1,9 @@
wavesurfer.js changelog
=======================
6.0.0 (unreleased)
------------------
- add image handling for `waveColor` and `progressColor` parameters (#2345)
califken marked this conversation as resolved.
Show resolved Hide resolved

5.3.0 (unreleased)
------------------
- add additional type to `waveColor` and `progressColor` parameters to support linear gradients (#2345)
Expand Down
Binary file added example/image-fill-styles/abstract.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/boxes.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/canyon.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/doors.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
173 changes: 173 additions & 0 deletions example/image-fill-styles/index.html
@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>wavesurfer.js | Image Fill Styles Example</title>
<link href="data:image/gif;" rel="icon" type="image/x-icon" />
<!-- Bootstrap -->
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="../css/style.css" />
<link rel="stylesheet" href="../css/ribbon.css" />
<link rel="screenshot" itemprop="screenshot" href="https://katspaugh.github.io/wavesurfer.js/example/screenshot.png" />
<!-- wavesurfer.js -->
<script src="../../dist/wavesurfer.js"></script>
<!-- Demo -->
<script src="main.js"></script>
<!-- highlight.js for syntax highlighting in this example -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/styles/default.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/11.2.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>
<style>#waveform {
z-index:-5;
califken marked this conversation as resolved.
Show resolved Hide resolved
margin-top:-150px;
}
.controls {
margin-top:-180px;
}
</style>
</head>
<body itemscope itemtype="http://schema.org/WebApplication">
<div class="container">
<div class="header">
<ul class="nav nav-pills pull-right">
<li><a href="/"><i class="glyphicon glyphicon-home"></i></a></li>
</ul>
<h1 itemprop="name">Image Fill Style</h1>
</div>
<p class="marketing">
Use any image element that has been loaded onto the page as the wave background.
</p>
<div id="demo">
<img width="100" src="water2.jpg" id="wavesurfer-water" style="display:none;" />
<img width="100" src="wood.jpg" id="wavesurfer-wood" style="display:none;" />
califken marked this conversation as resolved.
Show resolved Hide resolved
<div id="waveformwrapper">
<div id="waveform"></div>
</div>
<div class="controls">
<button id="waveform-btn">Play</button>
</div>
</div>
<div class="row marketing">
<h3>Image Fill Style example</h3>
<hr />
<p><b>Important: </b> You need to load the image before passing it to the WaveSurfer.create method, as illustrated below.</p>
<h4>HTML</h4>
<p>
<pre><code>&lt;!-- HTML --&gt;
&lt;!-- Load wave and progress images, set the style display properties to none. --&gt;
&lt;img width=&quot;100&quot; src=&quot;water2.jpg&quot; id=&quot;wavesurfer-water&quot; style=&quot;display:none;&quot; /&gt;
&lt;img width=&quot;100&quot; src=&quot;wood.jpg&quot; id=&quot;wavesurfer-wood&quot; style=&quot;display:none;&quot; /&gt;
&lt;div id=&quot;waveform&quot;&gt;&lt;/div&gt;</code></pre>
<h4>JS</h4>
<pre><code>/**
* JS
* Pass the image element as an argument for the waveColor (and/or progressColor) parameter.
*/
wavesurfer = WaveSurfer.create({
califken marked this conversation as resolved.
Show resolved Hide resolved
container: document.querySelector("#waveform"),
waveColor: document.querySelector("#wavesurfer-water"),
progressColor: document.querySelector("#wavesurfer-wood")
});
</code></pre>
</p>
</div>
<div class="row marketing">
<h3>More Image Fill Style examples</h3>
<hr />
<div>
<table padding="5">
<tr>
<td>
<img
src="tile3.jpg"
id="wavesurfer-tile2"
width="50"
/>
<img
src="tile1.jpg"
id="wavesurfer-tile1"
width="50"
/>
</td>
<td width="500">
<div id="waveform2"></div>
</td>
<td>
<button id="waveform-btn2">Play</button>
</td>
</tr>
<tr>
<td><img width="100" src="boxes.jpg" id="wavesurfer-boxes" /></td>
<td>
<div id="waveform3"></div>
</td>
<td>
<button id="waveform-btn3">Play</button>
</td>
</tr>
</table>
<h3>Mixing Image, Gradient and Color fill styles</h3>
<table>
<tr>
<td><img width="100" src="abstract.jpg" id="wavesurfer-abstract" /></td>
<td width="500">
<div id="waveform4"></div>
</td>
<td>
<button id="waveform-btn4">Play</button>
</td>
</tr>
<tr>
<td><img width="100" src="doors.jpg" id="wavesurfer-doors" /></td>
<td>
<div id="waveform5"></div>
</td>
<td>
<button id="waveform-btn5">Play</button>
</td>
</tr>
</table>
</div>
</div>
<h4>WaveSurfer Options</h4>
<p>
<code>waveColor</code> - string | array | HTMLImageElement - Can be CSS color value string, an array of colors, or an HTML Image Element.
</p>
<p>
<code>progressColor</code> - string | array | HTMLImageElement - Can be CSS color value string, an array of colors, or an HTML Image Element.
</p>
<div class="footer row">
<div class="col-sm-12">
<a rel="license" href="https://opensource.org/licenses/BSD-3-Clause"><img alt="BSD-3-Clause License" style="border-width:0" src="https://img.shields.io/badge/License-BSD%203--Clause-blue.svg" /></a>
</div>
<div class="col-sm-7">
<span xmlns:dct="http://purl.org/dc/terms/" href="http://purl.org/dc/dcmitype/Text" property="dct:title" rel="dct:type">wavesurfer.js</span> by <a href="https://github.com/katspaugh/wavesurfer.js">katspaugh</a> is licensed under a&nbsp;<a style="white-space: nowrap" rel="license" href="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause License</a>.
</div>
<div class="col-sm-5">
<div class="pull-right">
<noindex>
The audio file is from <a rel="nofollow" href="http://spokencorpora.ru/">spokencorpora.ru</a>, used
with permission.
</noindex>
</div>
</div>
</div>
</div>
<div class="github-fork-ribbon-wrapper right">
<div class="github-fork-ribbon">
<a itemprop="isBasedOnUrl" href="https://github.com/katspaugh/wavesurfer.js">Fork me on GitHub</a>
</div>
</div>
<script>
(function (i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date(); a = s.createElement(o),
m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');

ga('create', 'UA-50026819-1', 'wavesurfer.fm');
ga('send', 'pageview');
</script>
</body>
</html>
70 changes: 70 additions & 0 deletions example/image-fill-styles/main.js
@@ -0,0 +1,70 @@
"use strict";

// Create an instance
let wavesurfer = {};
thijstriemstra marked this conversation as resolved.
Show resolved Hide resolved

// Init & load audio file
document.addEventListener("DOMContentLoaded", function() {
wavesurfer = WaveSurfer.create({
container: document.querySelector("#waveform"),
height:500,
waveColor: document.querySelector("#wavesurfer-water"),
progressColor: document.querySelector("#wavesurfer-wood"),
cursorColor:'rgba(255,255,255,.5)',
cursorWidth: 20,
fillParent: true,
responsive: true
});
let wavesurfer2 = WaveSurfer.create({
container: document.querySelector("#waveform2"),
height: 150,
waveColor: document.querySelector("#wavesurfer-tile2"),
progressColor: document.querySelector("#wavesurfer-tile1")
});
let wavesurfer3 = WaveSurfer.create({
container: document.querySelector("#waveform3"),
waveColor: document.querySelector("#wavesurfer-boxes"),
progressColor: document.querySelector("#wavesurfer-boxes"),
barGap: 2,
barHeight: 1,
barMinHeight: 1,
barRadius: 2,
barWidth: 4,
height: 250
});
let wavesurfer4 = WaveSurfer.create({
container: document.querySelector("#waveform4"),
waveColor: document.querySelector("#wavesurfer-abstract"),
progressColor: ['rgba(0,255,255,.5)', 'blue', 'aqua']
});
let wavesurfer5 = WaveSurfer.create({
container: document.querySelector("#waveform5"),
waveColor: document.querySelector("#wavesurfer-doors"),
progressColor: '#000'
});

wavesurfer.load("../media/demo.mp3");
wavesurfer2.load("../media/demo.mp3");
wavesurfer3.load("../media/demo.mp3");
wavesurfer4.load("../media/demo.mp3");
wavesurfer5.load("../media/demo.mp3");

// Set the playhead to halfway through the media, as to demonstrate the colorProgress gradient
wavesurfer2.on("ready", () => wavesurfer2.seekTo(0.2));
wavesurfer3.on("ready", () => wavesurfer3.seekTo(0.8));
wavesurfer4.on("ready", () => wavesurfer4.seekTo(0.4));
wavesurfer5.on("ready", () => wavesurfer5.seekTo(0.3));

let waveformBtn = document.getElementById('waveform-btn');
waveformBtn.addEventListener('click', wavesurfer.playPause.bind(wavesurfer));
let waveformBtn2 = document.getElementById('waveform-btn2');
waveformBtn2.addEventListener('click', wavesurfer2.playPause.bind(wavesurfer2));

let waveformBtn3 = document.getElementById('waveform-btn3');
waveformBtn3.addEventListener('click', wavesurfer3.playPause.bind(wavesurfer3));
let waveformBtn4 = document.getElementById('waveform-btn4');
waveformBtn4.addEventListener('click', wavesurfer4.playPause.bind(wavesurfer4));
let waveformBtn5 = document.getElementById('waveform-btn5');
waveformBtn5.addEventListener('click', wavesurfer5.playPause.bind(wavesurfer5));

califken marked this conversation as resolved.
Show resolved Hide resolved
});
Binary file added example/image-fill-styles/rainbow.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/tile1.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/tile3.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/water2.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/image-fill-styles/wood.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/media/demo.mp3
Binary file not shown.
9 changes: 9 additions & 0 deletions spec/wavesurfer.spec.js
Expand Up @@ -279,6 +279,15 @@ describe('WaveSurfer/playback:', function() {
expect(waveColor).toEqual(colors);
});

/** @test {WaveSurfer#setWaveColorImage} */
it('allow setting waveColor Image', function() {
var img = new Image(); // Create new img element
califken marked this conversation as resolved.
Show resolved Hide resolved
img.src = "data:image/gif;base64,R0lGODlhCwALAIAAAAAA3pn/ZiH5BAEAAAEALAAAAAALAAsAAAIUhA+hkcuO4lmNVindo7qyrIXiGBYAOw==";
wavesurfer.setWaveColor(img);
const waveColor = wavesurfer.getWaveColor();
expect(waveColor).toEqual(img);
});

/** @test {WaveSurfer#getProgressColor} */
it('allow getting progressColor', function() {
const progressColor = wavesurfer.getProgressColor();
Expand Down
44 changes: 31 additions & 13 deletions src/drawer.canvasentry.js
Expand Up @@ -145,10 +145,14 @@ export default class CanvasEntry {

/**
* Set the fill styles for wave and progress
* @param {string|string[]} waveColor Fill color for the wave canvas,
* or an array of colors to apply as a gradient
* @param {?string|string[]} progressColor Fill color for the progress canvas,
* or an array of colors to apply as a gradient
* @param {string|string[]|CanvasImageSource} waveColor Fill color for
* the wave canvas, an array of colors to apply as a gradient, or a Canvas
* Image Source (HTML Image, SVG Image, HTML Video, HTML Canvas, Image
* Bitmap and Offscreen Canvas elements)
* @param {?string|string[]|CanvasImageSource} progressColor Fill color for
* the wave canvas, an array of colors to apply as a gradient, or a Canvas
* Image Source (HTML Image, SVG Image, HTML Video, HTML Canvas, Image
* Bitmap and Offscreen Canvas elements)
*/
setFillStyles(waveColor, progressColor) {
this.waveCtx.fillStyle = this.getFillStyle(this.waveCtx, waveColor);
Expand All @@ -159,26 +163,40 @@ export default class CanvasEntry {
}

/**
* Utility function to handle wave color arguments
* Utility function to determine the type and handle the argument passed to
* a color setting method
*
* When the color argument type is a string, it will be returned as is.
* Otherwise, it will be treated as an array, and a canvas gradient will
* be returned
* Return type is dependent on the color argument's type
*
* @since 5.3.0
thijstriemstra marked this conversation as resolved.
Show resolved Hide resolved
* @param {CanvasRenderingContext2D} ctx Rendering context of target canvas
* @param {string|string[]} color Fill color for the wave canvas, or an array of colors to apply as a gradient
* @returns {string|CanvasGradient} Returns a string fillstyle value, or a canvas gradient
* @param {string|string[]|CanvasImageSource} color A CSS color value as a
* string, an array of CSS color value strings, or a Canvas Image Source
* (HTML Image, SVG Image, HTML Video, HTML Canvas, Image Bitmap or
* Offscreen Canvas element)
califken marked this conversation as resolved.
Show resolved Hide resolved
* @returns {string|CanvasGradient|CanvasPattern} Returns a CSS color
* value string, a canvas gradient or a canvas pattern
*/
getFillStyle(ctx, color) {
// if the color argument is a string, handle it as a CSS color value
if (typeof color == 'string') {
califken marked this conversation as resolved.
Show resolved Hide resolved
return color;
}

const waveGradient = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height);
color.forEach((value, index) => waveGradient.addColorStop((index / color.length), value));
// if it is an array, handle it as a gradient
if (Array.isArray(color)) {
const waveGradient = ctx.createLinearGradient(0, 0, 0, ctx.canvas.height);
color.forEach((value, index) => waveGradient.addColorStop((index / color.length), value));
return waveGradient;
}

// if it is an object, handle it as a CanvasImageSource
if (typeof color == "object") {
califken marked this conversation as resolved.
Show resolved Hide resolved
return ctx.createPattern(color, "repeat");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

now that we have new methods, let's think about allowing user to change "repeat" value (and other settings in new method). Thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add an optional options parameter to setWaveColor, setWaveStyle, etc.. that could include options depending on the type of style being set. If it's a gradient, options for controlling the angle of the gradient. If it's a Canvas Image Source, the repeat value.

setWaveStyle(myCanvImgSrc, {repeat: 'repeat-x'});

Are you looking all the way back up to the WaveSurfer parameters object? Or just up to the new methods?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you looking all the way back up to the WaveSurfer parameters object? Or just up to the new methods?

Yep, both.

Copy link
Contributor Author

@califken califken Sep 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My recommendation is a new Wavesurfer Params object property called waveFillStyle, which would be an object type that could also be plugged into progressFillStyle, and eventually the wave background, the playhead, etc..)

  generateWaveform() {
    ...,
    waveFillStyle: {
      
      // only one of these (color, gradient, image, canvas, video) would be required
      color: {'red'},

      // each type of fill style would have its options as siblings of its data
      gradient: {
          colorstops: [
              [0.0,'red'],
              [0.5,'blue'],
              [1.0,'purple']
          ],
          coordinates: [0,0,500,500]
      },
      image: {
          src: document.querySelector("#image-element"),
          repeat: 'repeat'
      },

      // not yet, but eventually could pass any canvas as the style
      canvas: {
          element: document.querySelector("#canvas-element")
      },

      // I was able to pass in a canvas video as the background, it is pretty great,
      // but was quite a bit of setup, as in having to run an interval to update the canvas
      // from the video element, so still sorting the last pieces of that out before I 
      // push anything
      video: {
          element: document.querySelector("#video-canvas")
      }

  }

And keeping waveColor, etc.. as is for backwards compatibility.

What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's start with setWaveStyle(myCanvImgSrc, {repeat: 'repeat-x'}); like you suggested. The object passed to setWaveStyle can be called options. We can later decide what this object is called in the main wavesurfer options.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In setWaveStyle, the style is being set to the waveColor parameter, and then drawBuffer is called. How do I get access to this options object over in drawer.canvasentry.js's getFillStyle method?

From what I can tell, I will need to

  • set the options object as a params property
  • add that property as an argument to drawer.multicanvas.js's call to setFillStyles line 556
  • add the wave and progressStyleOptions parameter to drawer.canvasentry.js's setFillStyles method line 156
  • pass the new arguments to the getFillStyle calls lines 158 and 161
  • subsequently pass it getFillStyle's ctx.createPattern call, where the repeat option can be utilized.

Is this what you had in mind? It is what I am committing right now, and it works.

If this is not on track with what you were looking for, I will need more details on how else to make this happen. Let me know! Thanks

}

return waveGradient;
// if it passed none of the checks, return a default value
return '#999';
}

/**
Expand Down