块元素

块元素是指有外盒(outer box)和内容盒(content box)的元素。

SpriteJS Next的块元素继承基类Block,主要有Sprite、Label、Group三大类。

锚点 anchor

与旧版的SpriteJS一样,块元素可以设置不同的anchor值,用来表示参考点,anchor值的变化会影响块元素的定位和默认的transformOrigin

  1. const {Scene, Sprite, Gradient, Path} = spritejs;
  2. const container = document.getElementById('adaptive');
  3. const scene = new Scene({
  4. container,
  5. width: 600,
  6. height: 360,
  7. });
  8. const layer = scene.layer();
  9. const gradient = new Gradient({
  10. vector: [0, 0, 150, 150],
  11. colors: [
  12. {offset: 0, color: 'red'},
  13. {offset: 1, color: 'green'},
  14. ],
  15. });
  16. const box = new Sprite({
  17. anchor: [0.5, 0.5],
  18. size: [150, 150],
  19. pos: [300, 180],
  20. bgcolor: gradient,
  21. });
  22. layer.append(box);
  23. const cross = new Path('M-5 0L5 0M0 5L0 -5');
  24. cross.attr({
  25. pos: [300, 180],
  26. lineWidth: 2,
  27. strokeColor: 'blue',
  28. });
  29. layer.append(cross);
  30. box.animate([
  31. {rotate: 0},
  32. {rotate: 360},
  33. ], {
  34. iterations: Infinity,
  35. duration: 3000,
  36. });

在线运行

边框 border

与HTML元素类似,块元素可以设置边框,相关属性包括:

  • borderWidth 设置边框的宽度
  • borderColor 设置边框的颜色
  • borderDash 设置虚线框
  • borderDashOffset 设置虚线框偏移量
  • borderRadius 设置圆角
  1. const {Scene, Sprite} = spritejs;
  2. const container = document.getElementById('adaptive');
  3. const scene = new Scene({
  4. container,
  5. width: 600,
  6. height: 360,
  7. });
  8. const layer = scene.layer();
  9. const box = new Sprite({
  10. anchor: [0.5, 0.5],
  11. size: [150, 150],
  12. pos: [300, 180],
  13. bgcolor: 'white',
  14. borderWidth: 1,
  15. borderRadius: 20,
  16. });
  17. layer.append(box);

内边距 padding

与HTML元素类似,块元素可以设置内边距:

  • paddingTop
  • paddingRight
  • paddingBottom
  • paddingLeft
  1. const {Scene, Label} = spritejs;
  2. const container = document.getElementById('adaptive');
  3. const scene = new Scene({
  4. container,
  5. width: 600,
  6. height: 360,
  7. // contextType: '2d',
  8. });
  9. const layer = scene.layer();
  10. const box = new Label({
  11. text: 'SpriteJS',
  12. fontSize: '2rem',
  13. anchor: [0.5, 0.5],
  14. pos: [300, 180],
  15. bgcolor: 'white',
  16. borderWidth: 1,
  17. padding: 25,
  18. });
  19. layer.append(box);

盒子 boxSizing

与HTML元素类似,块元素可以通过修改boxSizing属性来设置元素宽高的计算方式。

块元素 - 图1

如图所示,一个块元素有四个“宽高”,分别是:

  • contentSize 内容的宽高
  • clientSize 内容的宽高+padding
  • borderSize 内容的宽高+padding+border的一半
  • offsetSize 内容的宽高+padding+border

box-sizing值为content-size(默认值)时,元素的width和height属性分别为:

  1. el.attribute.width = contentSize[0];
  2. el.attribute.height = contentSize[1];

box-sizing值为border-size时,元素的width和height属性分别为:

  1. el.attribute.width = offsetSize[0];
  2. el.attribute.width = offsetSize[1];

精灵 Sprite

Sprite元素是最常用的块元素,它可以设置texture属性。与旧版SpriteJS不同,旧版SpriteJS可以设置textures属性指定多张图片,新版 SpriteJS Next 则只能设置一张图片。

除了设置texture外,还可以设置sourceRect来剪裁图片,或者设置textureRect来将图片显示在精灵的指定位置。

如果要重复显示图片,可以设置精灵的textureRepeat属性为true,这个可以用来实现类似HTML中的平铺背景。

  1. const {Scene, Sprite} = spritejs;
  2. const container = document.getElementById('adaptive');
  3. const imgUrl = 'https://p5.ssl.qhimg.com/t01a2bd87890397464a.png';
  4. const scene = new Scene({
  5. container,
  6. width: 1200,
  7. height: 600,
  8. });
  9. const layer = scene.layer();
  10. const s1 = new Sprite({
  11. anchor: [0, 0.5],
  12. pos: [20, 300],
  13. bgcolor: 'white',
  14. borderWidth: 1,
  15. borderRadius: 20,
  16. texture: imgUrl,
  17. });
  18. layer.append(s1);
  19. const srcParts = [
  20. [0, 0, 190, 268],
  21. [0, 269, 190, 268],
  22. [191, 0, 190, 268],
  23. [191, 269, 190, 268],
  24. ];
  25. for(let i = 0; i < 2; i++) {
  26. for(let j = 0; j < 2; j++) {
  27. const sourceRect = srcParts[i * 2 + j];
  28. const x = 360 + i * 200;
  29. const y = j * 278;
  30. const s = new Sprite({
  31. x,
  32. y,
  33. texture: imgUrl,
  34. sourceRect,
  35. });
  36. layer.append(s);
  37. }
  38. }
  39. const s2 = new Sprite({
  40. anchor: [0, 0.5],
  41. pos: [720, 300],
  42. bgcolor: 'white',
  43. borderWidth: 1,
  44. borderRadius: 20,
  45. texture: imgUrl,
  46. textureRect: [0, 0, 190, 268],
  47. textureRepeat: true,
  48. });
  49. layer.append(s2);

文字 Label

基于性能考虑,SpriteJS Next 的 Label 元素比旧版本的SpriteJS的Label元素要弱化了不少。

新版本只支持单行文本,不支持自动排版。

  1. const {Scene, Label} = spritejs;
  2. const container = document.getElementById('adaptive');
  3. const scene = new Scene({
  4. container,
  5. width: 1200,
  6. height: 600,
  7. // contextType: '2d',
  8. });
  9. const layer = scene.layer();
  10. const text1 = new Label('SpriteJS.org');
  11. text1.attr({
  12. pos: [100, 40],
  13. fillColor: '#707',
  14. font: 'oblique small-caps bold 56px Arial',
  15. border: [2.5, '#ccc'],
  16. });
  17. layer.append(text1);
  18. const text2 = new Label('从前有座灵剑山');
  19. text2.attr({
  20. pos: [500, 20],
  21. fillColor: '#077',
  22. font: '64px "宋体"',
  23. lineHeight: 112,
  24. textAlign: 'center',
  25. padding: [0, 30],
  26. border: [2.5, '#ccc'],
  27. });
  28. layer.append(text2);
  29. const text3 = new Label('Hello');
  30. text3.attr({
  31. pos: [100, 240],
  32. strokeColor: 'red',
  33. font: 'bold oblique 70px Microsoft Yahei',
  34. strokeWidth: 1,
  35. textAlign: 'center',
  36. padding: [0, 30],
  37. border: [2.5, '#ccc'],
  38. });
  39. layer.append(text3);
  40. function createClockTexts(text, x, y) {
  41. const len = text.length;
  42. for(let i = 0; i < len; i++) {
  43. const char = text.charAt(i);
  44. const label = new Label(char);
  45. label.attr({
  46. anchor: [0.5, 4.5],
  47. pos: [x, y],
  48. font: 'bold 44px Arial',
  49. fillColor: '#37c',
  50. rotate: i * 360 / len,
  51. });
  52. layer.append(label);
  53. }
  54. }
  55. createClockTexts('Sprite.js JavaScript Canvas...', 700, 360);

分组 Group

SpriteJS Next 的Group比旧版简单一些,性能好很多,但功能上也不再有的clip和剪裁区域的功能。

  1. const {Scene, Group, Path} = spritejs;
  2. const container = document.getElementById('adaptive');
  3. const scene = new Scene({
  4. container,
  5. width: 1200,
  6. height: 600,
  7. });
  8. const layer = scene.layer();
  9. const arcD = 'M0 0L 50 0A50 50 0 0 1 43.3 25z';
  10. const group = new Group();
  11. group.attr({
  12. size: [300, 300],
  13. pos: [600, 300],
  14. anchor: [0.5, 0.5],
  15. bgcolor: '#cec',
  16. borderRadius: 150,
  17. });
  18. layer.append(group);
  19. for(let i = 0; i < 6; i++) {
  20. const arc = new Path();
  21. arc.attr({
  22. d: arcD,
  23. scale: 3,
  24. anchor: [0, 0.5],
  25. strokeColor: 'red',
  26. fillColor: `rgb(${i * 99 % 255}, 0, 0)`,
  27. rotate: i * 60,
  28. });
  29. group.append(arc);
  30. }
  31. group.animate([
  32. {rotate: 0},
  33. {rotate: 360},
  34. ], {
  35. duration: 3000,
  36. iterations: Infinity,
  37. });

SpriteSvg

SpriteSvg 元素继承自 Sprite 元素,可以给定一个 Svg 图片。

SpriteSvg 元素可以和 D3 配合,方便地渲染坐标轴。

  1. /* globals d3 */
  2. const {Scene, SpriteSvg} = spritejs;
  3. const container = document.getElementById('stage');
  4. const scene = new Scene({
  5. container,
  6. width: 1200,
  7. height: 1200,
  8. });
  9. const dataset = [125, 121, 127, 193, 309];
  10. const scale = d3.scaleLinear()
  11. .domain([100, d3.max(dataset)])
  12. .range([0, 500]);
  13. const colors = ['#fe645b', '#feb050', '#c2af87', '#81b848', '#55abf8'];
  14. const fglayer = scene.layer('fglayer');
  15. const s = d3.select(fglayer);
  16. document.querySelector('#stage canvas').style.backgroundColor = '#eee';
  17. const chart = s.selectAll('sprite')
  18. .data(dataset)
  19. .enter()
  20. .append('sprite')
  21. .attr('x', 450)
  22. .attr('y', (d, i) => {
  23. return 200 + i * 95;
  24. })
  25. .attr('width', 0)
  26. .attr('height', 80)
  27. .attr('bgcolor', '#ccc');
  28. chart.transition()
  29. .duration(2000)
  30. .attr('width', (d, i) => {
  31. return scale(d);
  32. })
  33. .attr('bgcolor', (d, i) => {
  34. return colors[i];
  35. });
  36. const axis = d3.axisBottom(scale).tickValues([100, 200, 300, 400]);
  37. const axisNode = new SpriteSvg({
  38. x: 420,
  39. y: 680,
  40. });
  41. d3.select(axisNode.svg)
  42. .attr('width', 600)
  43. .attr('height', 60)
  44. .append('g')
  45. .attr('transform', 'translate(30, 0)')
  46. .call(axis);
  47. axisNode.svg.children[0].setAttribute('font-size', 20);
  48. fglayer.append(axisNode);
  49. chart.on('click', (data) => {
  50. /* eslint-disable no-console */
  51. console.log(data, d3.event);
  52. /* eslint-enable no-console */
  53. });

此外,我们可以将SpriteSvg元素的flexible属性设为true,让SVG自适应元素大小。

  1. const container = document.getElementById('container');
  2. const scene = new spritejs.Scene({
  3. container,
  4. displayRatio: 2,
  5. width: 1200,
  6. height: 600,
  7. mode: 'stickyHeight',
  8. });
  9. const svgURL = 'https://s1.ssl.qhres.com/static/9bd74da7f533f462.svg';
  10. const fglayer = scene.layer('fglayer');
  11. const root = new spritejs.SpriteSvg(svgURL);
  12. root.attr({
  13. x: 100,
  14. y: 100,
  15. flexible: true,
  16. scale: 0.5,
  17. });
  18. fglayer.append(root);