I found the cause - in onResize() I was setting the size of the canvas to match the container. Doing that causes the canvas to be cleared. onResize() is called on the canvas even though it was not changing size - a sibling was changing size only. So it may be a minor bug in LayoutPanel in that it calls onResize() on all children during an animation, not just the children being animated.
I changed the onResize() to only set the pixel size of the canvas when it actually differed from the current size, which seems to fix it.
Code:
int width = this.getElement().getParentElement().getClientWidth();
int height = this.getElement().getParentElement().getClientHeight();
if (this.getCoordinateSpaceWidth() != width) this.setCoordinateSpaceWidth(width);
if (this.getCoordinateSpaceHeight() != height) this.setCoordinateSpaceHeight(height);