How to draw drop-down shadows in iOS
A CAShapeLayer uses a CGPathRef to draw it's stuff. So I have a star path, and I want a smooth drop shadow with a radius of about 15 units.开发者_如何学Go Probably there is some nice functionality in some new iPhone OS versions, but I need to do it myself for a old aged version of 3.0 (which most people still use).
I tried to do some REALLY nasty stuff: I created a for-loop and sequentially created like 15 of those paths, transform-scaling them step by step to become bigger. Then assigning them to a new created CAShapeLayer and decreasing it's alpha a little bit on every iteration. Not only that this scaling is mathematically incorrect and sucks (it should happen relative to the outline!), the shadow is not rounded and looks really ugly. That's why nice soft shadows have a radius.
The tips of a star shouldn't appear totally sharp after a shadow size of 15 units. They should be soft like cream. But in my ugly solution they're just as s harp as the star itself, since all I do is scale the star 15 times and decrease it's alpha 15 times. Ugly.
I wonder how the big guys do it? If you had an arbitrary path, and that path must throw a shadow, how does the algorithm to do that work? Probably the path would have to be expanded like 30 times, point-by-point relative to the tangent of the outline away from the filled part, and just by 0.5 units to have a nice blending.
Before I re-invent the wheel, maybe someone has a handy example or link?
A shadow is a translucent grayscale mask of the shape of an object blurred and offset.
CGContextSetShadowWithColor
and CGContextSetShadow
are how this is done on iPhone. You set the shadow then draw something and a shadow is also applied.
A CAShapeLayer does not have an easy option to apply a shadow. You will have to create a custom view or layer and set the shadow before drawing your shape.
I have not tried it, but the following might work:
@interface ShadowShapeLayer : CAShapeLayer
@end
@implementation ShadowShapeLayer
-(void) drawInContext:(CGContextRef)context {
CGContextSaveGState( context );
CGContextSetShadow( context , CGSizeMake( 5 , 5 ) , 15 );
[super drawInContext:context];
CGContextRestoreGState( context );
}
@end
Edit: Thanks Miser.
I asked myself the same question. I'm not an expert on this topic at all, but I had the following thought: Physically, one point of a drawing should result in a circular (or elliptical), semi-transparent shadow. So an entire drawing, which consists of multiple points, should result in the combination of a lot of such circular shadows.
So I painted a little shadow in Photoshop (brush tool, size 7, opacity 33%, color #3b3b3b). It's hardly visible:
Then I wrote a small HTML with Javascript just to try and see what it looks like (definitely not the ideal technique :-):
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Title</title>
<script type="text/javascript" language="javascript">
function pageload() {
var drawingContainerElem = document.getElementById("drawing-container");
var shadowContainerElem = document.getElementById("shadow-container");
this.drawDot = function(x, y) {
var imgElem = document.createElement("img");
imgElem.style.left = x + "px";
imgElem.style.top = y + "px";
imgElem.src = "blue-dot.png";
drawingContainerElem.appendChild(imgElem);
}
this.drawShadow = function(x, y) {
var imgElem = document.createElement("img");
imgElem.style.left = x + "px";
imgElem.style.top = y + "px";
imgElem.src = "soft-shadow.png";
shadowContainerElem.appendChild(imgElem);
}
this.drawDotAndShadow = function(x, y) {
drawShadow(x - 5, y - 1);
drawDot(x, y);
}
for (var x = 50; x < 70; x ++) {
for (var y = 50; y < 58; y ++) {
drawDotAndShadow(x, y);
}
}
for (var x = 0; x < 15; x ++) {
for (var y = 0; y < x; y ++) {
drawDotAndShadow(69 + 15 - x, 54 + y);
drawDotAndShadow(69 + 15 - x, 54 - y);
}
}
}
</script>
<style type="text/css">
#drawing-container {
position: absolute;
left: 2em;
top: 2em;
width: 400px;
height: 400px;
z-index: 2;
}
#shadow-container {
position: absolute;
left: 2em;
top: 2em;
width: 400px;
height: 400px;
z-index: 1;
}
#drawing-container img {
position: absolute;
}
#shadow-container img {
position: absolute;
}
</style>
</head>
<body onload="javascript:pageload()">
<div id="drawing-container"></div>
<div id="shadow-container"></div>
</body>
</html>
This is the result:
Probably, there's a lot of room for optimization, and of course you wouldn't really render it using javascript in this way... Maybe you can find a way how to render this efficiently on the iPhone? If yes, let me know!
Possible improvements:
- Make the center of the shadow circle darker (more opacity), and the rest lighter (less opacity) to achieve a core shadow.
- Scale the shadow: Make it a bit smaller, to achieve a depth effect.
精彩评论