绘图尺寸

我们知道,画布canvas的坐标范围它的宽高属性决定,而在网页中,canvas呈现在文档中的大小则由canvas对象的css样式决定,两者不一定相同。对应于spritejs,用resolution表示画布的宽高,而用viewport表示canvas呈现在文档中的宽高。

  1. const scene = new Scene('#coordinate', {viewport:[770, 300], resolution: [1540, 600]})

在一般情况下我们可以不设置viewport,这样的话默认的viewport会根据容器自动调整,便于我们按照不同窗口来适配我们的canvas。

比如我们定义一个相对自适应的元素:

  1. #adaptive {
  2. width: 50%;
  3. padding-bottom: 50%;
  4. }

坐标 - 图1

  1. const scene = new Scene('#adaptive');
  2. const layer = scene.layer();
  3. const [width, height] = layer.resolution;
  4. const path = new Path('M 297.29747,550.86823 C 283.52243,535.43191 249.1268,505.33855 220.86277,483.99412 C 137.11867,420.75228 125.72108,411.5999 91.719238,380.29088 C 29.03471,322.57071 2.413622,264.58086 2.5048478,185.95124 C 2.5493594,147.56739 5.1656152,132.77929 15.914734,110.15398 C 34.151433,71.768267 61.014996,43.244667 95.360052,25.799457 C 119.68545,13.443675 131.6827,7.9542046 172.30448,7.7296236 C 214.79777,7.4947896 223.74311,12.449347 248.73919,26.181459 C 279.1637,42.895777 310.47909,78.617167 316.95242,103.99205 L 320.95052,119.66445 L 330.81015,98.079942 C 386.52632,-23.892986 564.40851,-22.06811 626.31244,101.11153 C 645.95011,140.18758 648.10608,223.6247 630.69256,270.6244 C 607.97729,331.93377 565.31255,378.67493 466.68622,450.30098 C 402.0054,497.27462 328.80148,568.34684 323.70555,578.32901 C 317.79007,589.91654 323.42339,580.14491 297.29747,550.86823 z');
  5. path.attr({
  6. anchor: [0.5, 0.5],
  7. pos: [width / 2, height / 2],
  8. fillColor: 'red',
  9. scale: width / 800,
  10. });
  11. layer.append(path);

不设置viewport自动适配容器大小,只会在scene初始化的时候执行,如果我们希望在窗口大小改变的同时保持scene大小继续适配容器大小,那么我们可以将viewport手动设置为['auto', 'auto']

坐标 - 图2

把viewport设置为['auto', 'auto'],scene会自动注册一个resize事件到window上,当窗口大小改变时,触发viewport的更新。

  1. const scene = new Scene('#coordinate', {viewport: ['auto', 'auto'], resolution: [1540, 600]});
  2. const layer = scene.layer();
  3. const [width] = scene.resolution;
  4. const label = new Label(`resolution: ${[...scene.resolution]} | viewport: ${[...scene.viewport]}`);
  5. label.attr({
  6. anchor: [0.5, 0],
  7. pos: [width / 2, 10],
  8. font: '32px Arial',
  9. fillColor: '#aaa',
  10. });
  11. layer.append(label);
  12. function createBox(x, size) {
  13. const box = new Label(`${size}px`);
  14. const bgcolor = `rgb(${size % 128 + 100}, ${size % 66}, ${size % 77})`;
  15. box.attr({
  16. anchor: [0.5, 0],
  17. pos: [x, 100],
  18. size: [size, size],
  19. bgcolor,
  20. fillColor: '#eee',
  21. font: '24px Arial',
  22. textAlign: 'center',
  23. });
  24. return box;
  25. }
  26. for(let i = 1, x = 200; i <= 4; i++) {
  27. const box = createBox(x, i * 100);
  28. x += 100 * (i + 1);
  29. layer.append(box);
  30. }
  31. window.addEventListener('resize', () => {
  32. label.text = `resolution: ${[...scene.resolution]} | viewport: ${[...scene.viewport]}`;
  33. });

有时候,我们需要让canvas的宽度或高度其中一项自适应,但是我们又希望精灵元素保持宽高比例不变,此时我们可以在窗口大小改变的时候同动态修改Scene的resolution属性,一旦它被改变,所有Layer的resolution一同改变,并重新绘制元素。

坐标 - 图3

尝试改变窗口大小,可以看到绘制的鹬鸵的大小比例并没有被改变。

  1. const scene = new Scene('#adaptivesvg');
  2. scene.viewport = ['auto', 'auto'];
  3. const layer = scene.layer();
  4. function createKiwi(x) {
  5. const kiwi = new Sprite('https://s1.ssl.qhres.com/static/e6a7d82354f52374.svg');
  6. kiwi.attr({
  7. pos: [x, 10],
  8. size: [120, 100],
  9. });
  10. return kiwi;
  11. }
  12. for(let i = 0; i < 7; i++) {
  13. const kiwi = createKiwi(10 + i * 120);
  14. layer.append(kiwi);
  15. }
  16. window.addEventListener('resize', () => {
  17. const [w, h] = scene.viewport;
  18. scene.resolution = [w, h];
  19. });

注意:微信小程序版spritejs采用不同于标准版的方式来管理绘图尺寸,具体可参考高级用法:屏幕适配

锚点 anchor

在前面的例子中,我们看到Sprite元素有不同的定位方式,具体表现为不同的anchor值。比如例1是anchor:[0.5, 0.5],例2是anchor:[0.5, 0],例3没有设定,是默认值anchor:[0, 0]

在spritejs中,元素的anchor属性用来表示它的参考点,坐标定位、transform都是根据anchor值来设定的,默认值为[0, 0],即以元素的左上角位置为参考点,正常值取0~1之间,表示参考点坐标相对于元素宽高的比例,因此如果设置为[1, 1]则为右下角。

调整anchor值,看看元素有什么变化:

anchor-x: anchor-y:

坐标 - 图4

  1. const scene = new Scene('#anchor', {viewport: ['auto', 'auto'], resolution: [1540, 600]});
  2. const layer = scene.layer();
  3. const box = new Sprite({
  4. anchor: [0.5, 0.5],
  5. size: [200, 200],
  6. pos: [770, 300],
  7. gradients: {
  8. bgcolor: {
  9. vector: [0, 0, 200, 200],
  10. colors: [
  11. {offset: 0, color: 'red'},
  12. {offset: 1, color: 'green'},
  13. ],
  14. },
  15. },
  16. });
  17. layer.append(box);
  18. const cross = new Path('M0 10L20 10M10 0L10 20');
  19. cross.attr({
  20. anchor: [0.5, 0.5],
  21. pos: [770, 300],
  22. strokeColor: 'blue',
  23. lineWidth: 3,
  24. });
  25. layer.append(cross);
  26. const label = new Label('anchorX: 0.5, anchorY: 0.5');
  27. label.attr({
  28. pos: [20, 20],
  29. font: '26px Arial',
  30. fillColor: '#aaa',
  31. });
  32. layer.append(label);
  33. box.animate([
  34. {rotate: 0},
  35. {rotate: 360},
  36. ], {
  37. iterations: Infinity,
  38. duration: 3000,
  39. });
  40. const anchorX = document.getElementById('anchorX'),
  41. anchorY = document.getElementById('anchorY');
  42. anchorX.addEventListener('change', (evt) => {
  43. const target = evt.target,
  44. y = box.attr('anchor')[1];
  45. const value = target.value / 100;
  46. box.attr('anchor', [value, y]);
  47. label.text = `anchorX: ${value}, anchorY: ${y}`;
  48. });
  49. anchorY.addEventListener('change', (evt) => {
  50. const target = evt.target,
  51. x = box.attr('anchor')[0];
  52. const value = target.value / 100;
  53. box.attr('anchor', [x, value]);
  54. label.text = `anchorX: ${x}, anchorY: ${value}`;
  55. });